Skip to content

Commit

Permalink
Make dev-server less slow
Browse files Browse the repository at this point in the history
  • Loading branch information
jplatte committed Feb 17, 2024
1 parent eef856e commit 7f0ad4e
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 82 deletions.
15 changes: 0 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,4 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
# Enable some optimizations for specific dependencies.
# Otherwise, debug builds are unbearably slow.
[profile.dev.package]
flate2 = { opt-level = 2 }
regex-automata = { opt-level = 2 }

fancy-regex = { opt-level = 1 }
regex-syntax = { opt-level = 1 }
syntect = { opt-level = 1 }
124 changes: 78 additions & 46 deletions components/core/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,116 @@ use anyhow::Context as _;
use bumpalo_herd::Herd;
use camino::Utf8Path;
use fs_err::{self as fs};
#[cfg(feature = "syntax-highlighting")]
use once_cell::sync::OnceCell;
use rayon::iter::{ParallelBridge as _, ParallelIterator as _};
use tracing::{error, warn};
use walkdir::WalkDir;

use crate::{
config::Config,
content::{ContentProcessor, ContentProcessorContext},
content::{ContentProcessor, ContentProcessorContext, SyntaxHighlighter},
template::load_templates,
};

mod output_dir;

pub(crate) use self::output_dir::OutputDirManager;

pub fn build(config: &Config, include_drafts: bool) -> ExitCode {
fn build_inner(
config: &Config,
include_drafts: bool,
output_dir_mgr: &OutputDirManager,
) -> anyhow::Result<bool> {
let alloc = Herd::new();
let template_env = load_templates(&alloc)?;
let ctx =
ContentProcessorContext::new(config, include_drafts, template_env, output_dir_mgr);
rayon::scope(|scope| ContentProcessor::new(scope, &ctx).run())?;
Ok(ctx.did_error.load(Ordering::Relaxed))
pub struct Build {
config: Config,
include_drafts: bool,
#[cfg(feature = "syntax-highlighting")]
syntax_highlighter: OnceCell<SyntaxHighlighter>,
}

impl Build {
pub fn new(config: Config, include_drafts: bool) -> Self {
Self {
config,
include_drafts,
#[cfg(feature = "syntax-highlighting")]
syntax_highlighter: OnceCell::new(),
}
}

fn copy_assets(output_dir_mgr: &OutputDirManager) -> anyhow::Result<()> {
WalkDir::new("theme/assets/").into_iter().par_bridge().try_for_each(|entry| {
let entry = entry.context("walking asset directory")?;
if entry.file_type().is_dir() {
return Ok(());
}
pub fn config(&self) -> &Config {
&self.config
}

let Some(utf8_path) = Utf8Path::from_path(entry.path()) else {
warn!("Skipping non-utf8 file `{}`", entry.path().display());
return Ok(());
};
pub fn run(&self) -> ExitCode {
fn copy_assets(output_dir_mgr: &OutputDirManager) -> anyhow::Result<()> {
WalkDir::new("theme/assets/").into_iter().par_bridge().try_for_each(|entry| {
let entry = entry.context("walking asset directory")?;
if entry.file_type().is_dir() {
return Ok(());
}

let rel_path =
utf8_path.strip_prefix("theme/assets/").context("invalid WalkDir item")?;
let output_path = output_dir_mgr.output_path(rel_path, utf8_path)?;
let Some(utf8_path) = Utf8Path::from_path(entry.path()) else {
warn!("Skipping non-utf8 file `{}`", entry.path().display());
return Ok(());
};

fs::copy(utf8_path, output_path).context("copying asset")?;
Ok(())
})
}
let rel_path =
utf8_path.strip_prefix("theme/assets/").context("invalid WalkDir item")?;
let output_path = output_dir_mgr.output_path(rel_path, utf8_path)?;

let output_dir_mgr = OutputDirManager::new(config.output_dir.clone());
fs::copy(utf8_path, output_path).context("copying asset")?;
Ok(())
})
}

let (r1, r2) = rayon::join(
|| build_inner(config, include_drafts, &output_dir_mgr),
|| copy_assets(&output_dir_mgr),
);
let output_dir_mgr = OutputDirManager::new(self.config.output_dir.clone());

match (r1, r2) {
(Err(e1), Err(e2)) => {
error!("{e1:#}");
error!("{e2:#}");
ExitCode::FAILURE
}
(Ok(_), Err(e)) | (Err(e), Ok(_)) => {
error!("{e:#}");
ExitCode::FAILURE
let (r1, r2) =
rayon::join(|| self.run_inner(&output_dir_mgr), || copy_assets(&output_dir_mgr));

match (r1, r2) {
(Err(e1), Err(e2)) => {
error!("{e1:#}");
error!("{e2:#}");
ExitCode::FAILURE
}
(Ok(_), Err(e)) | (Err(e), Ok(_)) => {
error!("{e:#}");
ExitCode::FAILURE
}
(Ok(true), Ok(())) => ExitCode::FAILURE,
(Ok(false), Ok(())) => ExitCode::SUCCESS,
}
(Ok(true), Ok(())) => ExitCode::FAILURE,
(Ok(false), Ok(())) => ExitCode::SUCCESS,
}

fn run_inner(&self, output_dir_mgr: &OutputDirManager) -> anyhow::Result<bool> {
let alloc = Herd::new();
let template_env = load_templates(&alloc)?;
let ctx = ContentProcessorContext::new(
&self.config,
self.include_drafts,
template_env,
output_dir_mgr,
#[cfg(feature = "syntax-highlighting")]
&self.syntax_highlighter,
);
rayon::scope(|scope| ContentProcessor::new(scope, &ctx).run())?;
Ok(ctx.did_error.load(Ordering::Relaxed))
}
}

pub fn build(config: Config, include_drafts: bool) -> ExitCode {
Build::new(config, include_drafts).run()
}

pub fn dump(config: Config) -> ExitCode {
let output_dir_mgr = OutputDirManager::new("".into());
#[cfg(feature = "syntax-highlighting")]
let syntax_highlighter = OnceCell::new();
let ctx = ContentProcessorContext::new(
&config,
true,
minijinja::Environment::empty(),
&output_dir_mgr,
#[cfg(feature = "syntax-highlighting")]
&syntax_highlighter,
);

let res = rayon::scope(|scope| ContentProcessor::new(scope, &ctx).dump());
Expand Down
9 changes: 5 additions & 4 deletions components/core/src/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ use tracing::{error, instrument, warn};

#[cfg(feature = "markdown")]
use self::markdown::markdown_to_html;
#[cfg(feature = "syntax-highlighting")]
use self::syntax_highlighting::SyntaxHighlighter;
use crate::{
build::OutputDirManager, config::Config, frontmatter::parse_frontmatter,
metadata::metadata_env, template::functions,
Expand All @@ -35,6 +33,8 @@ mod markdown;
mod syntax_highlighting;

pub(crate) use self::file_config::{ContentFileConfig, ProcessContent};
#[cfg(feature = "syntax-highlighting")]
pub(crate) use self::syntax_highlighting::SyntaxHighlighter;

pub(crate) struct ContentProcessor<'c, 's, 'sc> {
// FIXME: args, template_env, syntax_highlighter (in ctx) plus render_scope
Expand Down Expand Up @@ -278,7 +278,7 @@ pub(crate) struct ContentProcessorContext<'a> {
include_drafts: bool,
template_env: minijinja::Environment<'a>,
#[cfg(feature = "syntax-highlighting")]
syntax_highlighter: OnceCell<SyntaxHighlighter>,
syntax_highlighter: &'a OnceCell<SyntaxHighlighter>,
output_dir_mgr: &'a OutputDirManager,
pub(crate) did_error: AtomicBool,
}
Expand All @@ -289,13 +289,14 @@ impl<'a> ContentProcessorContext<'a> {
include_drafts: bool,
template_env: minijinja::Environment<'a>,
output_dir_mgr: &'a OutputDirManager,
#[cfg(feature = "syntax-highlighting")] syntax_highlighter: &'a OnceCell<SyntaxHighlighter>,
) -> Self {
Self {
config,
include_drafts,
template_env,
#[cfg(feature = "syntax-highlighting")]
syntax_highlighter: OnceCell::new(),
syntax_highlighter,
output_dir_mgr,
did_error: AtomicBool::new(false),
}
Expand Down
2 changes: 1 addition & 1 deletion components/core/src/content/syntax_highlighting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use syntect::{
};
use tracing::{error, warn};

pub(super) struct SyntaxHighlighter {
pub(crate) struct SyntaxHighlighter {
syntaxset: SyntaxSet,
themes: BTreeMap<String, Theme>,
}
Expand Down
2 changes: 0 additions & 2 deletions components/dev_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ edition = "2021"
[dependencies]
anyhow.workspace = true
camino = "1.1.6"
futures-util = { version = "0.3.30", features = ["alloc", "std"] }
fs-err.workspace = true
hinoki_core = { path = "../core" }
hyper = { version = "1.0.0", features = ["http1", "http2", "server"] }
hyper-util = { version = "0.1.2", features = ["http1", "http2", "tokio", "server-auto", "service"] }
notify = "6.1.1"
notify-debouncer-full = "0.3.1"
Expand Down
48 changes: 40 additions & 8 deletions components/dev_server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::{
fmt,
net::{Ipv6Addr, SocketAddr},
process::ExitCode,
sync::Arc,
time::Duration,
time::{Duration, Instant},
};

use camino::Utf8Path;
use fs_err as fs;
use hinoki_core::{build::build, Config};
use hinoki_core::{build::Build, Config};
use hyper_util::service::TowerToHyperService;
use tempfile::tempdir;
use tower_http::services::ServeDir;
Expand All @@ -32,17 +33,23 @@ pub fn run(config: Config) -> ExitCode {
async fn run_inner(mut config: Config) -> anyhow::Result<()> {
let output_dir = tempdir()?;
config.output_dir = output_dir.path().to_owned().try_into()?;
build(&config, true);

let _watch_guard = start_watch(&config)?;
let build = Build::new(config, true);
let begin = Instant::now();
build.run();
info!("Built site in {}", FormatDuration(begin.elapsed()));

let config = build.config().clone();
let _watch_guard = start_watch(build)?;
serve(&config).await?;

Ok(())
}

/// Start file notification watcher.
///
/// Dropping the returned value stops the watcher thread.
fn start_watch(config: &Config) -> anyhow::Result<impl Drop> {
fn start_watch(build: Build) -> anyhow::Result<impl Drop> {
use notify::{
event::{CreateKind, ModifyKind},
EventKind, RecursiveMode, Watcher,
Expand All @@ -54,7 +61,6 @@ fn start_watch(config: &Config) -> anyhow::Result<impl Drop> {
let current_dir = fs::canonicalize(".")?;

let mut debouncer = new_debouncer(DEBOUNCE_DURATION, None, {
let config = config.clone();
let current_dir = current_dir.clone();
move |res: DebounceEventResult| match res {
Err(errors) => {
Expand Down Expand Up @@ -84,7 +90,7 @@ fn start_watch(config: &Config) -> anyhow::Result<impl Drop> {
}
};

rel_path.starts_with(&config.path)
rel_path.starts_with(&build.config().path)
|| rel_path.starts_with("content")
|| rel_path.starts_with("theme")
});
Expand All @@ -93,7 +99,9 @@ fn start_watch(config: &Config) -> anyhow::Result<impl Drop> {
});

if !events.is_empty() {
build(&config, true);
let begin = Instant::now();
build.run();
info!("Rebuilt site in {}", FormatDuration(begin.elapsed()));
}
}
}
Expand Down Expand Up @@ -129,3 +137,27 @@ async fn serve(config: &Config) -> anyhow::Result<()> {
});
}
}

struct FormatDuration(Duration);

impl fmt::Display for FormatDuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let duration = self.0;
let total_secs = duration.as_secs();
if let hours @ 1.. = total_secs / 3600 {
let minutes = total_secs / 60 % 60;
return write!(f, "{hours}h {minutes}min");
}
if let minutes @ 1.. = total_secs / 60 {
let secs = total_secs % 60;
return write!(f, "{minutes}min {secs}s");
}

let millis = duration.as_millis();
if total_secs > 0 {
write!(f, "{total_secs}s ")?;
}

write!(f, "{millis}ms")
}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn main() -> ExitCode {
};

match args.command {
Command::Build(args) => build(&config, args.include_drafts),
Command::Build(args) => build(config, args.include_drafts),
Command::DumpMetadata => dump(config),
#[cfg(feature = "dev-server")]
Command::Serve => hinoki_dev_server::run(config),
Expand Down

0 comments on commit 7f0ad4e

Please sign in to comment.