From 68646e9e5fd6731106c47696dbe04a6945996197 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 09:34:36 -0700 Subject: [PATCH] rustdoc: run css and html minifier at build instead of runtime This way, adding a bunch of comments to the JS files won't make rustdoc slower. --- src/librustdoc/Cargo.toml | 1 + src/librustdoc/build.rs | 24 +++++++++++++++---- src/librustdoc/config.rs | 4 ++-- src/librustdoc/html/render/write_shared.rs | 10 ++------ src/librustdoc/html/static_files.rs | 28 ++++++++-------------- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 70749f7cb17a0..d9bd11267dadd 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -33,6 +33,7 @@ features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] [build-dependencies] sha2 = "0.10.8" +minifier = { version = "0.3.2", default-features = false } [dev-dependencies] expect-test = "1.4.0" diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 69337fb1d2504..119836257fddf 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -1,3 +1,6 @@ +use std::str; + +use sha2::Digest; fn main() { // generate sha256 files // this avoids having to perform hashing at runtime @@ -35,14 +38,27 @@ fn main() { for path in files { let inpath = format!("html/{path}"); println!("cargo::rerun-if-changed={inpath}"); - let bytes = std::fs::read(inpath).expect("static path exists"); - use sha2::Digest; - let bytes = sha2::Sha256::digest(bytes); - let mut digest = format!("-{bytes:x}"); + let data_bytes = std::fs::read(&inpath).expect("static path exists"); + let hash_bytes = sha2::Sha256::digest(&data_bytes); + let mut digest = format!("-{hash_bytes:x}"); digest.truncate(9); let outpath = std::path::PathBuf::from(format!("{out_dir}/{path}.sha256")); std::fs::create_dir_all(outpath.parent().expect("all file paths are in a directory")) .expect("should be able to write to out_dir"); std::fs::write(&outpath, digest.as_bytes()).expect("write to out_dir"); + let minified_path = std::path::PathBuf::from(format!("{out_dir}/{path}.min")); + if path.ends_with(".js") || path.ends_with(".css") { + let minified: String = if path.ends_with(".css") { + minifier::css::minify(str::from_utf8(&data_bytes).unwrap()) + .unwrap() + .to_string() + .into() + } else { + minifier::js::minify(str::from_utf8(&data_bytes).unwrap()).to_string().into() + }; + std::fs::write(&minified_path, minified.as_bytes()).expect("write to out_dir"); + } else { + std::fs::copy(&inpath, &minified_path).unwrap(); + } } } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 80bc6cebd2aa9..e9b0fdcc7d856 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -458,7 +458,7 @@ impl Options { let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } @@ -607,7 +607,7 @@ impl Options { let mut themes = Vec::new(); if matches.opt_present("theme") { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fb6f3bc2c76ed..57d07c05c1189 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -207,14 +207,8 @@ fn write_static_files( if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) { static_files::for_each(|f: &static_files::StaticFile| { let filename = static_dir.join(f.output_filename()); - let contents: &[u8]; - let contents_vec: Vec; - if opt.disable_minification { - contents = f.bytes; - } else { - contents_vec = f.minified(); - contents = &contents_vec; - }; + let contents: &[u8] = + if opt.disable_minification { f.src_bytes } else { f.minified_bytes }; fs::write(&filename, contents).map_err(|e| PathError::new(e, &filename)) })?; } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 6457ac731cb76..b877a8526406a 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -8,26 +8,18 @@ use std::{fmt, str}; pub(crate) struct StaticFile { pub(crate) filename: PathBuf, - pub(crate) bytes: &'static [u8], + pub(crate) src_bytes: &'static [u8], + pub(crate) minified_bytes: &'static [u8], } impl StaticFile { - fn new(filename: &str, bytes: &'static [u8], sha256: &'static str) -> StaticFile { - Self { filename: static_filename(filename, sha256), bytes } - } - - pub(crate) fn minified(&self) -> Vec { - let extension = match self.filename.extension() { - Some(e) => e, - None => return self.bytes.to_owned(), - }; - if extension == "css" { - minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into() - } else if extension == "js" { - minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into() - } else { - self.bytes.to_owned() - } + fn new( + filename: &str, + src_bytes: &'static [u8], + minified_bytes: &'static [u8], + sha256: &'static str, + ) -> StaticFile { + Self { filename: static_filename(filename, sha256), src_bytes, minified_bytes } } pub(crate) fn output_filename(&self) -> &Path { @@ -68,7 +60,7 @@ macro_rules! static_files { // sha256 files are generated in build.rs pub(crate) static STATIC_FILES: std::sync::LazyLock = std::sync::LazyLock::new(|| StaticFiles { - $($field: StaticFile::new($file_path, include_bytes!($file_path), include_str!(concat!(env!("OUT_DIR"), "/", $file_path, ".sha256"))),)+ + $($field: StaticFile::new($file_path, include_bytes!($file_path), include_bytes!(concat!(env!("OUT_DIR"), "/", $file_path, ".min")), include_str!(concat!(env!("OUT_DIR"), "/", $file_path, ".sha256"))),)+ }); pub(crate) fn for_each(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<(), E> {