Add minify support (#1146)

* Doc add a missing arg to `get_taxonomy_url` (#1139)

This feature is already exist, but not in the doc yet

Related #766

* Add minify support

* Add documentation

* Code review

* Fix error in documentation

* Update minify-html to 0.3.6

* Move minify into write_content function

* Fix multiple calls to minify()

* Add test for minified output

* Fix breaking test

Co-authored-by: Ken <2770219+ken0x0a@users.noreply.github.com>
This commit is contained in:
areille 2020-08-28 17:39:19 +00:00 committed by GitHub
parent d571dea8c3
commit 0df3631b3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 4 deletions

12
Cargo.lock generated
View file

@ -1320,6 +1320,17 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "minify-html"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b917ef1eb405ca3b1a489608f36392f0718ff4740bbc6f7d89edd4ec4c869fb7"
dependencies = [
"aho-corasick",
"lazy_static",
"memchr",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.3.7" version = "0.3.7"
@ -2177,6 +2188,7 @@ dependencies = [
"lazy_static", "lazy_static",
"library", "library",
"link_checker", "link_checker",
"minify-html",
"rayon", "rayon",
"sass-rs", "sass-rs",
"search", "search",

View file

@ -74,6 +74,8 @@ pub struct Config {
/// Whether to compile the `sass` directory and output the css files into the static folder /// Whether to compile the `sass` directory and output the css files into the static folder
pub compile_sass: bool, pub compile_sass: bool,
/// Whether to minify the html output
pub minify_html: bool,
/// Whether to build the search index for the content /// Whether to build the search index for the content
pub build_search_index: bool, pub build_search_index: bool,
/// A list of file glob patterns to ignore when processing the content folder. Defaults to none. /// A list of file glob patterns to ignore when processing the content folder. Defaults to none.
@ -320,6 +322,7 @@ impl Default for Config {
hard_link_static: false, hard_link_static: false,
taxonomies: Vec::new(), taxonomies: Vec::new(),
compile_sass: false, compile_sass: false,
minify_html: false,
mode: Mode::Build, mode: Mode::Build,
build_search_index: false, build_search_index: false,
ignored_content: Vec::new(), ignored_content: Vec::new(),

View file

@ -8,6 +8,7 @@ include = ["src/**/*"]
[dependencies] [dependencies]
tera = "1" tera = "1"
glob = "0.3" glob = "0.3"
minify-html = "0.3.6"
rayon = "1" rayon = "1"
serde = "1" serde = "1"
serde_derive = "1" serde_derive = "1"

View file

@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, RwLock};
use glob::glob; use glob::glob;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use minify_html::{truncate, Cfg};
use rayon::prelude::*; use rayon::prelude::*;
use tera::{Context, Tera}; use tera::{Context, Tera};
@ -445,6 +446,21 @@ impl Site {
html html
} }
/// Minifies html content
fn minify(&self, html: String) -> Result<String> {
let cfg = &Cfg { minify_js: false };
let mut input_bytes = html.as_bytes().to_vec();
match truncate(&mut input_bytes, cfg) {
Ok(_len) => match std::str::from_utf8(&mut input_bytes) {
Ok(result) => Ok(result.to_string()),
Err(err) => bail!("Failed to convert bytes to string : {}", err),
},
Err(minify_error) => {
bail!("Failed to truncate html at character {}:", minify_error.position);
}
}
}
/// Copy the main `static` folder and the theme `static` folder if a theme is used /// Copy the main `static` folder and the theme `static` folder if a theme is used
pub fn copy_static_directories(&self) -> Result<()> { pub fn copy_static_directories(&self) -> Result<()> {
// The user files will overwrite the theme files // The user files will overwrite the theme files
@ -511,10 +527,19 @@ impl Site {
create_directory(&current_path)?; create_directory(&current_path)?;
} }
let final_content = if !filename.ends_with("html") || !self.config.minify_html {
content
} else {
match self.minify(content) {
Ok(minified_content) => minified_content,
Err(error) => bail!(error),
}
};
match self.build_mode { match self.build_mode {
BuildMode::Disk => { BuildMode::Disk => {
let end_path = current_path.join(filename); let end_path = current_path.join(filename);
create_file(&end_path, &content)?; create_file(&end_path, &final_content)?;
} }
BuildMode::Memory => { BuildMode::Memory => {
let path = if filename != "index.html" { let path = if filename != "index.html" {
@ -527,7 +552,7 @@ impl Site {
} }
.trim_end_matches('/') .trim_end_matches('/')
.to_owned(); .to_owned();
&SITE_CONTENT.write().unwrap().insert(path, content); &SITE_CONTENT.write().unwrap().insert(path, final_content);
} }
} }
@ -543,8 +568,12 @@ impl Site {
let output = page.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; let output = page.render_html(&self.tera, &self.config, &self.library.read().unwrap())?;
let content = self.inject_livereload(output); let content = self.inject_livereload(output);
let components: Vec<&str> = page.path.split('/').collect(); let components: Vec<&str> = page.path.split('/').collect();
let current_path = let current_path = self.write_content(
self.write_content(&components, "index.html", content, !page.assets.is_empty())?; &components,
"index.html",
content,
!page.assets.is_empty(),
)?;
// Copy any asset we found previously into the same directory as the index.html // Copy any asset we found previously into the same directory as the index.html
for asset in &page.assets { for asset in &page.assets {

View file

@ -712,6 +712,18 @@ fn can_build_site_custom_builtins_from_theme() {
assert!(file_contains!(public, "404.html", "Oops")); assert!(file_contains!(public, "404.html", "Oops"));
} }
#[test]
fn can_build_site_with_html_minified() {
let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| {
site.config.minify_html = true;
(site, true)
});
assert!(&public.exists());
assert!(file_exists!(public, "index.html"));
assert!(file_contains!(public, "index.html", "<!DOCTYPE html><html lang=en><head><meta charset=UTF-8>"));
}
#[test] #[test]
fn can_ignore_markdown_content() { fn can_ignore_markdown_content() {
let (_, _tmp_dir, public) = build_site("test_site"); let (_, _tmp_dir, public) = build_site("test_site");

View file

@ -85,6 +85,9 @@ languages = []
# When set to "true", the Sass files in the `sass` directory are compiled. # When set to "true", the Sass files in the `sass` directory are compiled.
compile_sass = false compile_sass = false
# When set to "true", the HTML output files will be minified
minify_html = false
# A list of glob patterns specifying asset files to ignore when the content # A list of glob patterns specifying asset files to ignore when the content
# directory is processed. Defaults to none, which means that all asset files are # directory is processed. Defaults to none, which means that all asset files are
# copied over to the `public` directory. # copied over to the `public` directory.