@@ -4,7 +4,7 @@ | |||||
### Breaking | ### Breaking | ||||
- Pages with draft=true are now only loaded/rendered in `zola serve` | |||||
- Add `--drafts` flag to `build`, `serve` and `check` to load drafts. Drafts are never loaded by default anymore | |||||
### Other | ### Other | ||||
- Add `--open` flag to open server URL in default browser | - Add `--open` flag to open server URL in default browser | ||||
@@ -17,7 +17,7 @@ | |||||
- Taxonomies can now have the same name in multiple languages | - Taxonomies can now have the same name in multiple languages | ||||
- `zola init` can now be create sites inside the current directory | - `zola init` can now be create sites inside the current directory | ||||
- Fix table of contents generation for deep heading levels | - Fix table of contents generation for deep heading levels | ||||
- Add `lang` in all templates context except sitemap, robots etc | |||||
- Add `lang` in all templates context except sitemap, robots | |||||
- Add `lang` parameter to `get_taxonomy` and `get_taxonomy_url` | - Add `lang` parameter to `get_taxonomy` and `get_taxonomy_url` | ||||
- Rebuild whole site on changes in `themes` changes | - Rebuild whole site on changes in `themes` changes | ||||
- Add one-dark syntax highlighting theme | - Add one-dark syntax highlighting theme | ||||
@@ -249,7 +249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | dependencies = [ | ||||
"html5ever 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", | "html5ever 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -1334,7 +1334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
[[package]] | [[package]] | ||||
name = "maplit" | name = "maplit" | ||||
version = "1.0.1" | |||||
version = "1.0.2" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
[[package]] | [[package]] | ||||
@@ -1748,7 +1748,7 @@ name = "pest_meta" | |||||
version = "2.1.1" | version = "2.1.1" | ||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", | "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -3375,7 +3375,7 @@ dependencies = [ | |||||
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" | "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" | ||||
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" | "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" | ||||
"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" | "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" | ||||
"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" | |||||
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" | |||||
"checksum markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" | "checksum markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" | ||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | ||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" | ||||
@@ -13,7 +13,7 @@ use theme::Theme; | |||||
use utils::fs::read_file_with_error; | use utils::fs::read_file_with_error; | ||||
// We want a default base url for tests | // We want a default base url for tests | ||||
static DEFAULT_BASE_URL: &'static str = "http://a-website.com"; | |||||
static DEFAULT_BASE_URL: &str = "http://a-website.com"; | |||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||||
pub enum Mode { | pub enum Mode { | ||||
@@ -24,7 +24,7 @@ pub struct PageFrontMatter { | |||||
/// The converted date into a (year, month, day) tuple | /// The converted date into a (year, month, day) tuple | ||||
#[serde(default, skip_deserializing)] | #[serde(default, skip_deserializing)] | ||||
pub datetime_tuple: Option<(i32, u32, u32)>, | pub datetime_tuple: Option<(i32, u32, u32)>, | ||||
/// Whether this page is a draft and should be ignored for pagination etc | |||||
/// Whether this page is a draft | |||||
pub draft: bool, | pub draft: bool, | ||||
/// The page slug. Will be used instead of the filename if present | /// The page slug. Will be used instead of the filename if present | ||||
/// Can't be an empty string if present | /// Can't be an empty string if present | ||||
@@ -7,7 +7,7 @@ use errors::Result; | |||||
use super::{InsertAnchor, SortBy}; | use super::{InsertAnchor, SortBy}; | ||||
static DEFAULT_PAGINATE_PATH: &'static str = "page"; | |||||
static DEFAULT_PAGINATE_PATH: &str = "page"; | |||||
/// The front matter of every section | /// The front matter of every section | ||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||||
@@ -23,7 +23,7 @@ use regex::Regex; | |||||
use errors::{Error, Result}; | use errors::{Error, Result}; | ||||
use utils::fs as ufs; | use utils::fs as ufs; | ||||
static RESIZED_SUBDIR: &'static str = "processed_images"; | |||||
static RESIZED_SUBDIR: &str = "processed_images"; | |||||
lazy_static! { | lazy_static! { | ||||
pub static ref RESIZED_FILENAME: Regex = | pub static ref RESIZED_FILENAME: Regex = | ||||
@@ -117,9 +117,6 @@ impl<'a> Paginator<'a> { | |||||
for key in self.all_pages { | for key in self.all_pages { | ||||
let page = library.get_page_by_key(*key); | let page = library.get_page_by_key(*key); | ||||
if page.is_draft() { | |||||
continue; | |||||
} | |||||
current_page.push(page.to_serialized_basic(library)); | current_page.push(page.to_serialized_basic(library)); | ||||
if current_page.len() == self.paginate_by { | if current_page.len() == self.paginate_by { | ||||
@@ -283,7 +280,7 @@ mod tests { | |||||
assert_eq!(paginator.pagers[0].path, "posts/"); | assert_eq!(paginator.pagers[0].path, "posts/"); | ||||
assert_eq!(paginator.pagers[1].index, 2); | assert_eq!(paginator.pagers[1].index, 2); | ||||
assert_eq!(paginator.pagers[1].pages.len(), 1); | |||||
assert_eq!(paginator.pagers[1].pages.len(), 2); | |||||
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/page/2/"); | assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/page/2/"); | ||||
assert_eq!(paginator.pagers[1].path, "posts/page/2/"); | assert_eq!(paginator.pagers[1].path, "posts/page/2/"); | ||||
} | } | ||||
@@ -300,7 +297,7 @@ mod tests { | |||||
assert_eq!(paginator.pagers[0].path, ""); | assert_eq!(paginator.pagers[0].path, ""); | ||||
assert_eq!(paginator.pagers[1].index, 2); | assert_eq!(paginator.pagers[1].index, 2); | ||||
assert_eq!(paginator.pagers[1].pages.len(), 1); | |||||
assert_eq!(paginator.pagers[1].pages.len(), 2); | |||||
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/page/2/"); | assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/page/2/"); | ||||
assert_eq!(paginator.pagers[1].path, "page/2/"); | assert_eq!(paginator.pagers[1].path, "page/2/"); | ||||
} | } | ||||
@@ -352,7 +349,7 @@ mod tests { | |||||
assert_eq!(paginator.pagers[0].path, "tags/something"); | assert_eq!(paginator.pagers[0].path, "tags/something"); | ||||
assert_eq!(paginator.pagers[1].index, 2); | assert_eq!(paginator.pagers[1].index, 2); | ||||
assert_eq!(paginator.pagers[1].pages.len(), 1); | |||||
assert_eq!(paginator.pagers[1].pages.len(), 2); | |||||
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/tags/something/page/2/"); | assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/tags/something/page/2/"); | ||||
assert_eq!(paginator.pagers[1].path, "tags/something/page/2/"); | assert_eq!(paginator.pagers[1].path, "tags/something/page/2/"); | ||||
} | } | ||||
@@ -317,7 +317,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
} | } | ||||
if let Some(e) = error { | if let Some(e) = error { | ||||
return Err(e); | |||||
Err(e) | |||||
} else { | } else { | ||||
Ok(Rendered { | Ok(Rendered { | ||||
summary_len: if has_summary { html.find(CONTINUE_READING) } else { None }, | summary_len: if has_summary { html.find(CONTINUE_READING) } else { None }, | ||||
@@ -33,14 +33,14 @@ fn insert_into_parent(potential_parent: Option<&mut Header>, header: &Header) -> | |||||
match potential_parent { | match potential_parent { | ||||
None => { | None => { | ||||
// No potential parent to insert into so it needs to be insert higher | // No potential parent to insert into so it needs to be insert higher | ||||
return false; | |||||
}, | |||||
false | |||||
} | |||||
Some(parent) => { | Some(parent) => { | ||||
let diff = header.level - parent.level; | let diff = header.level - parent.level; | ||||
if diff <= 0 { | if diff <= 0 { | ||||
// Heading is same level or higher so we don't insert here | // Heading is same level or higher so we don't insert here | ||||
return false; | return false; | ||||
} | |||||
} | |||||
if diff == 1 { | if diff == 1 { | ||||
// We have a direct child of the parent | // We have a direct child of the parent | ||||
parent.children.push(header.clone()); | parent.children.push(header.clone()); | ||||
@@ -51,7 +51,7 @@ fn insert_into_parent(potential_parent: Option<&mut Header>, header: &Header) -> | |||||
// No, we need to insert it here | // No, we need to insert it here | ||||
parent.children.push(header.clone()); | parent.children.push(header.clone()); | ||||
} | } | ||||
return true; | |||||
true | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -61,23 +61,9 @@ fn insert_into_parent(potential_parent: Option<&mut Header>, header: &Header) -> | |||||
pub fn make_table_of_contents(headers: Vec<Header>) -> Vec<Header> { | pub fn make_table_of_contents(headers: Vec<Header>) -> Vec<Header> { | ||||
let mut toc = vec![]; | let mut toc = vec![]; | ||||
for header in headers { | for header in headers { | ||||
if toc.is_empty() { | |||||
// First header, nothing to compare it with | |||||
// First header or we try to insert the current header in a previous one | |||||
if toc.is_empty() || !insert_into_parent(toc.iter_mut().last(), &header) { | |||||
toc.push(header); | toc.push(header); | ||||
continue; | |||||
} | |||||
// We try to insert the current header in a previous one | |||||
match insert_into_parent(toc.iter_mut().last(), &header) { | |||||
true => { | |||||
// Header was successfully inserted as a child of a previous element | |||||
continue; | |||||
}, | |||||
false => { | |||||
// Couldn't insert in a previous header, so it's a top-level header | |||||
toc.push(header); | |||||
continue; | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -63,6 +63,8 @@ pub struct Site { | |||||
pub permalinks: HashMap<String, String>, | pub permalinks: HashMap<String, String>, | ||||
/// Contains all pages and sections of the site | /// Contains all pages and sections of the site | ||||
pub library: Arc<RwLock<Library>>, | pub library: Arc<RwLock<Library>>, | ||||
/// Whether to load draft pages | |||||
include_drafts: bool, | |||||
} | } | ||||
impl Site { | impl Site { | ||||
@@ -131,6 +133,7 @@ impl Site { | |||||
static_path, | static_path, | ||||
taxonomies: Vec::new(), | taxonomies: Vec::new(), | ||||
permalinks: HashMap::new(), | permalinks: HashMap::new(), | ||||
include_drafts: false, | |||||
// We will allocate it properly later on | // We will allocate it properly later on | ||||
library: Arc::new(RwLock::new(Library::new(0, 0, false))), | library: Arc::new(RwLock::new(Library::new(0, 0, false))), | ||||
}; | }; | ||||
@@ -138,6 +141,12 @@ impl Site { | |||||
Ok(site) | Ok(site) | ||||
} | } | ||||
/// Set the site to load the drafts. | |||||
/// Needs to be called before loading it | |||||
pub fn include_drafts(&mut self) { | |||||
self.include_drafts = true; | |||||
} | |||||
/// The index sections are ALWAYS at those paths | /// The index sections are ALWAYS at those paths | ||||
/// There are one index section for the basic language + 1 per language | /// There are one index section for the basic language + 1 per language | ||||
fn index_section_paths(&self) -> Vec<(PathBuf, Option<String>)> { | fn index_section_paths(&self) -> Vec<(PathBuf, Option<String>)> { | ||||
@@ -233,8 +242,8 @@ impl Site { | |||||
let mut pages_insert_anchors = HashMap::new(); | let mut pages_insert_anchors = HashMap::new(); | ||||
for page in pages { | for page in pages { | ||||
let p = page?; | let p = page?; | ||||
// Draft pages are not rendered in zola build so we just discard them | |||||
if p.meta.draft && !self.config.is_in_serve_mode() { | |||||
// Should draft pages be ignored? | |||||
if p.meta.draft && !self.include_drafts { | |||||
continue; | continue; | ||||
} | } | ||||
pages_insert_anchors.insert( | pages_insert_anchors.insert( | ||||
@@ -525,7 +534,7 @@ impl Site { | |||||
self.tera.register_function("trans", global_fns::Trans::new(self.config.clone())); | self.tera.register_function("trans", global_fns::Trans::new(self.config.clone())); | ||||
self.tera.register_function( | self.tera.register_function( | ||||
"get_taxonomy_url", | "get_taxonomy_url", | ||||
global_fns::GetTaxonomyUrl::new(&self.config.default_language,&self.taxonomies), | |||||
global_fns::GetTaxonomyUrl::new(&self.config.default_language, &self.taxonomies), | |||||
); | ); | ||||
} | } | ||||
@@ -540,7 +549,11 @@ impl Site { | |||||
); | ); | ||||
self.tera.register_function( | self.tera.register_function( | ||||
"get_taxonomy", | "get_taxonomy", | ||||
global_fns::GetTaxonomy::new(&self.config.default_language, self.taxonomies.clone(), self.library.clone()), | |||||
global_fns::GetTaxonomy::new( | |||||
&self.config.default_language, | |||||
self.taxonomies.clone(), | |||||
self.library.clone(), | |||||
), | |||||
); | ); | ||||
} | } | ||||
@@ -189,9 +189,10 @@ fn can_build_site_without_live_reload() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_build_site_with_live_reload() { | |||||
fn can_build_site_with_live_reload_and_drafts() { | |||||
let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { | let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { | ||||
site.enable_live_reload(1000); | site.enable_live_reload(1000); | ||||
site.include_drafts(); | |||||
(site, true) | (site, true) | ||||
}); | }); | ||||
@@ -230,7 +231,9 @@ fn can_build_site_with_live_reload() { | |||||
r#"<a name="continue-reading"></a>"# | r#"<a name="continue-reading"></a>"# | ||||
)); | )); | ||||
assert_eq!(file_exists!(public, "posts/draft/index.html"), false); | |||||
// Drafts are included | |||||
assert!(file_exists!(public, "posts/draft/index.html")); | |||||
assert!(file_contains!(public, "sitemap.xml", "draft")); | |||||
} | } | ||||
#[test] | #[test] | ||||
@@ -94,8 +94,8 @@ impl ResizeImage { | |||||
} | } | ||||
} | } | ||||
static DEFAULT_OP: &'static str = "fill"; | |||||
static DEFAULT_FMT: &'static str = "auto"; | |||||
static DEFAULT_OP: &str = "fill"; | |||||
static DEFAULT_FMT: &str = "auto"; | |||||
const DEFAULT_Q: u8 = 75; | const DEFAULT_Q: u8 = 75; | ||||
impl TeraFn for ResizeImage { | impl TeraFn for ResizeImage { | ||||
@@ -203,8 +203,9 @@ impl TeraFn for GetTaxonomyUrl { | |||||
args.get("name"), | args.get("name"), | ||||
"`get_taxonomy_url` requires a `name` argument with a string value" | "`get_taxonomy_url` requires a `name` argument with a string value" | ||||
); | ); | ||||
let lang = optional_arg!(String, args.get("lang"), "`get_taxonomy`: `lang` must be a string") | |||||
.unwrap_or_else(|| self.default_lang.clone()); | |||||
let lang = | |||||
optional_arg!(String, args.get("lang"), "`get_taxonomy`: `lang` must be a string") | |||||
.unwrap_or_else(|| self.default_lang.clone()); | |||||
let container = match self.taxonomies.get(&format!("{}-{}", kind, lang)) { | let container = match self.taxonomies.get(&format!("{}-{}", kind, lang)) { | ||||
Some(c) => c, | Some(c) => c, | ||||
@@ -296,7 +297,11 @@ pub struct GetTaxonomy { | |||||
default_lang: String, | default_lang: String, | ||||
} | } | ||||
impl GetTaxonomy { | impl GetTaxonomy { | ||||
pub fn new(default_lang: &str, all_taxonomies: Vec<Taxonomy>, library: Arc<RwLock<Library>>) -> Self { | |||||
pub fn new( | |||||
default_lang: &str, | |||||
all_taxonomies: Vec<Taxonomy>, | |||||
library: Arc<RwLock<Library>>, | |||||
) -> Self { | |||||
let mut taxonomies = HashMap::new(); | let mut taxonomies = HashMap::new(); | ||||
for taxo in all_taxonomies { | for taxo in all_taxonomies { | ||||
taxonomies.insert(format!("{}-{}", taxo.kind.name, taxo.kind.lang), taxo); | taxonomies.insert(format!("{}-{}", taxo.kind.name, taxo.kind.lang), taxo); | ||||
@@ -312,8 +317,9 @@ impl TeraFn for GetTaxonomy { | |||||
"`get_taxonomy` requires a `kind` argument with a string value" | "`get_taxonomy` requires a `kind` argument with a string value" | ||||
); | ); | ||||
let lang = optional_arg!(String, args.get("lang"), "`get_taxonomy`: `lang` must be a string") | |||||
.unwrap_or_else(|| self.default_lang.clone()); | |||||
let lang = | |||||
optional_arg!(String, args.get("lang"), "`get_taxonomy`: `lang` must be a string") | |||||
.unwrap_or_else(|| self.default_lang.clone()); | |||||
match self.taxonomies.get(&format!("{}-{}", kind, lang)) { | match self.taxonomies.get(&format!("{}-{}", kind, lang)) { | ||||
Some(t) => Ok(to_value(t.to_serialized(&self.library.read().unwrap())).unwrap()), | Some(t) => Ok(to_value(t.to_serialized(&self.library.read().unwrap())).unwrap()), | ||||
@@ -408,8 +414,8 @@ mod tests { | |||||
let tags_fr = Taxonomy { kind: taxo_config_fr, items: vec![tag_fr] }; | let tags_fr = Taxonomy { kind: taxo_config_fr, items: vec![tag_fr] }; | ||||
let taxonomies = vec![tags.clone(), tags_fr.clone()]; | let taxonomies = vec![tags.clone(), tags_fr.clone()]; | ||||
let static_fn = GetTaxonomy::new(&config.default_language, taxonomies.clone(), library.clone()) | |||||
; | |||||
let static_fn = | |||||
GetTaxonomy::new(&config.default_language, taxonomies.clone(), library.clone()); | |||||
// can find it correctly | // can find it correctly | ||||
let mut args = HashMap::new(); | let mut args = HashMap::new(); | ||||
args.insert("kind".to_string(), to_value("tags").unwrap()); | args.insert("kind".to_string(), to_value("tags").unwrap()); | ||||
@@ -55,7 +55,7 @@ date = | |||||
# will not be rendered. | # will not be rendered. | ||||
weight = 0 | weight = 0 | ||||
# A draft page is only rendered in `zola serve`, they are ignored in `zola build` and `zola check` | |||||
# A draft page is only loaded if the `--drafts` flag is passed to `zola build`, `zola serve` or `zola check` | |||||
draft = false | draft = false | ||||
# If filled, it will use that slug instead of the filename to make up the URL | # If filled, it will use that slug instead of the filename to make up the URL | ||||
@@ -56,6 +56,8 @@ You can also point to another config file than `config.toml` like so - the posit | |||||
$ zola --config config.staging.toml build | $ zola --config config.staging.toml build | ||||
``` | ``` | ||||
By defaults, drafts are not loaded. If you wish to include them, pass the `--drafts` flag. | |||||
## serve | ## serve | ||||
This will build and serve the site using a local server. You can also specify | This will build and serve the site using a local server. You can also specify | ||||
@@ -95,12 +97,16 @@ You can also point to another config file than `config.toml` like so - the posit | |||||
$ zola --config config.staging.toml serve | $ zola --config config.staging.toml serve | ||||
``` | ``` | ||||
By defaults, drafts are not loaded. If you wish to include them, pass the `--drafts` flag. | |||||
### check | ### check | ||||
The check subcommand will try to build all pages just like the build command would, but without writing any of the | The check subcommand will try to build all pages just like the build command would, but without writing any of the | ||||
results to disk. Additionally, it will also check all external links present in Markdown files by trying to fetch | results to disk. Additionally, it will also check all external links present in Markdown files by trying to fetch | ||||
them (links present in the template files will not be checked). | them (links present in the template files will not be checked). | ||||
By defaults, drafts are not loaded. If you wish to include them, pass the `--drafts` flag. | |||||
## Colored output | ## Colored output | ||||
Any of the three commands will emit colored output if your terminal supports it. | Any of the three commands will emit colored output if your terminal supports it. | ||||
@@ -36,6 +36,10 @@ pub fn build_cli() -> App<'static, 'static> { | |||||
.default_value("public") | .default_value("public") | ||||
.takes_value(true) | .takes_value(true) | ||||
.help("Outputs the generated site in the given path"), | .help("Outputs the generated site in the given path"), | ||||
Arg::with_name("drafts") | |||||
.long("drafts") | |||||
.takes_value(false) | |||||
.help("Include drafts when loading the site"), | |||||
]), | ]), | ||||
SubCommand::with_name("serve") | SubCommand::with_name("serve") | ||||
.about("Serve the site. Rebuild and reload on change automatically") | .about("Serve the site. Rebuild and reload on change automatically") | ||||
@@ -66,6 +70,10 @@ pub fn build_cli() -> App<'static, 'static> { | |||||
.long("watch-only") | .long("watch-only") | ||||
.takes_value(false) | .takes_value(false) | ||||
.help("Do not start a server, just re-build project on changes"), | .help("Do not start a server, just re-build project on changes"), | ||||
Arg::with_name("drafts") | |||||
.long("drafts") | |||||
.takes_value(false) | |||||
.help("Include drafts when loading the site"), | |||||
Arg::with_name("open") | Arg::with_name("open") | ||||
.short("O") | .short("O") | ||||
.long("open") | .long("open") | ||||
@@ -74,5 +82,11 @@ pub fn build_cli() -> App<'static, 'static> { | |||||
]), | ]), | ||||
SubCommand::with_name("check") | SubCommand::with_name("check") | ||||
.about("Try building the project without rendering it. Checks links") | .about("Try building the project without rendering it. Checks links") | ||||
.args(&[ | |||||
Arg::with_name("drafts") | |||||
.long("drafts") | |||||
.takes_value(false) | |||||
.help("Include drafts when loading the site"), | |||||
]) | |||||
]) | ]) | ||||
} | } |
@@ -5,12 +5,20 @@ use site::Site; | |||||
use console; | use console; | ||||
pub fn build(config_file: &str, base_url: Option<&str>, output_dir: &str) -> Result<()> { | |||||
pub fn build( | |||||
config_file: &str, | |||||
base_url: Option<&str>, | |||||
output_dir: &str, | |||||
include_drafts: bool, | |||||
) -> Result<()> { | |||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | ||||
site.set_output_path(output_dir); | site.set_output_path(output_dir); | ||||
if let Some(b) = base_url { | if let Some(b) = base_url { | ||||
site.set_base_url(b.to_string()); | site.set_base_url(b.to_string()); | ||||
} | } | ||||
if include_drafts { | |||||
site.include_drafts(); | |||||
} | |||||
site.load()?; | site.load()?; | ||||
console::notify_site_size(&site); | console::notify_site_size(&site); | ||||
console::warn_about_ignored_pages(&site); | console::warn_about_ignored_pages(&site); | ||||
@@ -6,7 +6,12 @@ use site::Site; | |||||
use console; | use console; | ||||
pub fn check(config_file: &str, base_path: Option<&str>, base_url: Option<&str>) -> Result<()> { | |||||
pub fn check( | |||||
config_file: &str, | |||||
base_path: Option<&str>, | |||||
base_url: Option<&str>, | |||||
include_drafts: bool, | |||||
) -> Result<()> { | |||||
let bp = base_path.map(PathBuf::from).unwrap_or_else(|| env::current_dir().unwrap()); | let bp = base_path.map(PathBuf::from).unwrap_or_else(|| env::current_dir().unwrap()); | ||||
let mut site = Site::new(bp, config_file)?; | let mut site = Site::new(bp, config_file)?; | ||||
// Force the checking of external links | // Force the checking of external links | ||||
@@ -14,6 +19,9 @@ pub fn check(config_file: &str, base_path: Option<&str>, base_url: Option<&str>) | |||||
if let Some(b) = base_url { | if let Some(b) = base_url { | ||||
site.set_base_url(b.to_string()); | site.set_base_url(b.to_string()); | ||||
} | } | ||||
if include_drafts { | |||||
site.include_drafts(); | |||||
} | |||||
site.load()?; | site.load()?; | ||||
console::check_site_summary(&site); | console::check_site_summary(&site); | ||||
console::warn_about_ignored_pages(&site); | console::warn_about_ignored_pages(&site); | ||||
@@ -59,16 +59,14 @@ pub fn is_directory_quasi_empty(path: &Path) -> Result<bool> { | |||||
pub fn create_new_project(name: &str) -> Result<()> { | pub fn create_new_project(name: &str) -> Result<()> { | ||||
let path = Path::new(name); | let path = Path::new(name); | ||||
// Better error message than the rust default | // Better error message than the rust default | ||||
if path.exists() { | |||||
if !is_directory_quasi_empty(&path)? { | |||||
if name == "." { | |||||
bail!("The current directory is not an empty folder (hidden files are ignored)."); | |||||
} else { | |||||
bail!( | |||||
"`{}` is not an empty folder (hidden files are ignored).", | |||||
path.to_string_lossy().to_string() | |||||
) | |||||
} | |||||
if path.exists() && !is_directory_quasi_empty(&path)? { | |||||
if name == "." { | |||||
bail!("The current directory is not an empty folder (hidden files are ignored)."); | |||||
} else { | |||||
bail!( | |||||
"`{}` is not an empty folder (hidden files are ignored).", | |||||
path.to_string_lossy().to_string() | |||||
) | |||||
} | } | ||||
} | } | ||||
@@ -118,6 +118,7 @@ fn create_new_site( | |||||
output_dir: &str, | output_dir: &str, | ||||
base_url: &str, | base_url: &str, | ||||
config_file: &str, | config_file: &str, | ||||
include_drafts: bool, | |||||
) -> Result<(Site, String)> { | ) -> Result<(Site, String)> { | ||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | ||||
@@ -132,6 +133,9 @@ fn create_new_site( | |||||
site.config.enable_serve_mode(); | site.config.enable_serve_mode(); | ||||
site.set_base_url(base_url); | site.set_base_url(base_url); | ||||
site.set_output_path(output_dir); | site.set_output_path(output_dir); | ||||
if include_drafts { | |||||
site.include_drafts(); | |||||
} | |||||
site.load()?; | site.load()?; | ||||
site.enable_live_reload(port); | site.enable_live_reload(port); | ||||
console::notify_site_size(&site); | console::notify_site_size(&site); | ||||
@@ -148,9 +152,11 @@ pub fn serve( | |||||
config_file: &str, | config_file: &str, | ||||
watch_only: bool, | watch_only: bool, | ||||
open: bool, | open: bool, | ||||
include_drafts: bool, | |||||
) -> Result<()> { | ) -> Result<()> { | ||||
let start = Instant::now(); | let start = Instant::now(); | ||||
let (mut site, address) = create_new_site(interface, port, output_dir, base_url, config_file)?; | |||||
let (mut site, address) = | |||||
create_new_site(interface, port, output_dir, base_url, config_file, include_drafts)?; | |||||
console::report_elapsed_time(start); | console::report_elapsed_time(start); | ||||
// Setup watchers | // Setup watchers | ||||
@@ -375,18 +381,21 @@ pub fn serve( | |||||
ChangeKind::StaticFiles => copy_static(&site, &path, &partial_path), | ChangeKind::StaticFiles => copy_static(&site, &path, &partial_path), | ||||
ChangeKind::Sass => reload_sass(&site, &path, &partial_path), | ChangeKind::Sass => reload_sass(&site, &path, &partial_path), | ||||
ChangeKind::Themes => { | ChangeKind::Themes => { | ||||
console::info("-> Themes changed. The whole site will be reloaded."); | |||||
console::info( | |||||
"-> Themes changed. The whole site will be reloaded.", | |||||
); | |||||
site = create_new_site( | site = create_new_site( | ||||
interface, | interface, | ||||
port, | port, | ||||
output_dir, | output_dir, | ||||
base_url, | base_url, | ||||
config_file, | config_file, | ||||
include_drafts, | |||||
) | ) | ||||
.unwrap() | .unwrap() | ||||
.0; | .0; | ||||
rebuild_done_handling(&broadcaster, Ok(()), "/x.js"); | rebuild_done_handling(&broadcaster, Ok(()), "/x.js"); | ||||
}, | |||||
} | |||||
ChangeKind::Config => { | ChangeKind::Config => { | ||||
console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | 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( | site = create_new_site( | ||||
@@ -395,6 +404,7 @@ pub fn serve( | |||||
output_dir, | output_dir, | ||||
base_url, | base_url, | ||||
config_file, | config_file, | ||||
include_drafts, | |||||
) | ) | ||||
.unwrap() | .unwrap() | ||||
.0; | .0; | ||||
@@ -432,18 +442,21 @@ pub fn serve( | |||||
(ChangeKind::StaticFiles, p) => copy_static(&site, &path, &p), | (ChangeKind::StaticFiles, p) => copy_static(&site, &path, &p), | ||||
(ChangeKind::Sass, p) => reload_sass(&site, &path, &p), | (ChangeKind::Sass, p) => reload_sass(&site, &path, &p), | ||||
(ChangeKind::Themes, _) => { | (ChangeKind::Themes, _) => { | ||||
console::info("-> Themes changed. The whole site will be reloaded."); | |||||
console::info( | |||||
"-> Themes changed. The whole site will be reloaded.", | |||||
); | |||||
site = create_new_site( | site = create_new_site( | ||||
interface, | interface, | ||||
port, | port, | ||||
output_dir, | output_dir, | ||||
base_url, | base_url, | ||||
config_file, | config_file, | ||||
include_drafts, | |||||
) | ) | ||||
.unwrap() | .unwrap() | ||||
.0; | .0; | ||||
rebuild_done_handling(&broadcaster, Ok(()), "/x.js"); | rebuild_done_handling(&broadcaster, Ok(()), "/x.js"); | ||||
}, | |||||
} | |||||
(ChangeKind::Config, _) => { | (ChangeKind::Config, _) => { | ||||
console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | 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( | site = create_new_site( | ||||
@@ -452,6 +465,7 @@ pub fn serve( | |||||
output_dir, | output_dir, | ||||
base_url, | base_url, | ||||
config_file, | config_file, | ||||
include_drafts, | |||||
) | ) | ||||
.unwrap() | .unwrap() | ||||
.0; | .0; | ||||
@@ -48,7 +48,12 @@ fn main() { | |||||
console::info("Building site..."); | console::info("Building site..."); | ||||
let start = Instant::now(); | let start = Instant::now(); | ||||
let output_dir = matches.value_of("output_dir").unwrap(); | let output_dir = matches.value_of("output_dir").unwrap(); | ||||
match cmd::build(config_file, matches.value_of("base_url"), output_dir) { | |||||
match cmd::build( | |||||
config_file, | |||||
matches.value_of("base_url"), | |||||
output_dir, | |||||
matches.is_present("drafts"), | |||||
) { | |||||
Ok(()) => console::report_elapsed_time(start), | Ok(()) => console::report_elapsed_time(start), | ||||
Err(e) => { | Err(e) => { | ||||
console::unravel_errors("Failed to build the site", &e); | console::unravel_errors("Failed to build the site", &e); | ||||
@@ -67,6 +72,7 @@ fn main() { | |||||
}; | }; | ||||
let watch_only = matches.is_present("watch_only"); | let watch_only = matches.is_present("watch_only"); | ||||
let open = matches.is_present("open"); | let open = matches.is_present("open"); | ||||
let include_drafts = matches.is_present("drafts"); | |||||
// Default one | // Default one | ||||
if port != 1111 && !watch_only && !port_is_available(port) { | if port != 1111 && !watch_only && !port_is_available(port) { | ||||
@@ -85,7 +91,16 @@ fn main() { | |||||
let output_dir = matches.value_of("output_dir").unwrap(); | let output_dir = matches.value_of("output_dir").unwrap(); | ||||
let base_url = matches.value_of("base_url").unwrap(); | let base_url = matches.value_of("base_url").unwrap(); | ||||
console::info("Building site..."); | console::info("Building site..."); | ||||
match cmd::serve(interface, port, output_dir, base_url, config_file, watch_only, open) { | |||||
match cmd::serve( | |||||
interface, | |||||
port, | |||||
output_dir, | |||||
base_url, | |||||
config_file, | |||||
watch_only, | |||||
open, | |||||
include_drafts, | |||||
) { | |||||
Ok(()) => (), | Ok(()) => (), | ||||
Err(e) => { | Err(e) => { | ||||
console::unravel_errors("", &e); | console::unravel_errors("", &e); | ||||
@@ -100,6 +115,7 @@ fn main() { | |||||
config_file, | config_file, | ||||
matches.value_of("base_path"), | matches.value_of("base_path"), | ||||
matches.value_of("base_url"), | matches.value_of("base_url"), | ||||
matches.is_present("drafts"), | |||||
) { | ) { | ||||
Ok(()) => console::report_elapsed_time(start), | Ok(()) => console::report_elapsed_time(start), | ||||
Err(e) => { | Err(e) => { | ||||