feat: basic image optimization

This commit is contained in:
Maciej Jur 2024-07-07 13:09:14 +02:00
parent ad9c0b1505
commit 1c0c2c49c6
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
14 changed files with 482 additions and 56 deletions

3
.gitignore vendored
View file

@ -26,3 +26,6 @@ target/
# JavaScript
js/**/node_modules/
# Hashed images
.hash

297
Cargo.lock generated
View file

@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
@ -99,12 +114,38 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
name = "async-trait"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "biblatex"
version = "0.9.3"
@ -130,6 +171,18 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -141,9 +194,15 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.15.4"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e"
[[package]]
name = "byteorder"
@ -266,6 +325,16 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "clap_mangen"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50dde5bc0c853d6248de457e5eb6e5a674a54b93810a34ded88d882ca1fe2de"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "codemap"
version = "0.1.3"
@ -311,6 +380,25 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
@ -492,6 +580,12 @@ dependencies = [
"libc",
]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "generic-array"
version = "0.14.7"
@ -524,6 +618,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "glob"
version = "0.3.1"
@ -636,6 +736,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "html-escape"
version = "0.2.13"
@ -729,12 +835,13 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.2.5"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
"rayon",
"serde",
]
@ -842,6 +949,24 @@ version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libdeflate-sys"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "669ea17f9257bcb48c09c7ee4bef3957777504acffac557263e20c11001977bc"
dependencies = [
"cc",
]
[[package]]
name = "libdeflater"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dfd6424f7010ee0a3416f1d796d0450e3ad3ac237a237644f728277c4ded016"
dependencies = [
"libdeflate-sys",
]
[[package]]
name = "libquickjs-sys"
version = "0.9.0"
@ -864,6 +989,15 @@ version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.11"
@ -931,12 +1065,41 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25be21376a772d15f97ae789845340a9651d3c4246ff5ebb6a2b35f9c37bd31"
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oxipng"
version = "9.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f398c53eb34e0cf71d9e0bc676cfa7c611e3844dd14ab05e92fb7b423c98ecf"
dependencies = [
"bitvec",
"clap",
"clap_mangen",
"crossbeam-channel",
"filetime",
"indexmap",
"libdeflater",
"log",
"rayon",
"rgb",
"rustc-hash",
"rustc_version",
]
[[package]]
name = "paste"
version = "1.0.14"
@ -991,6 +1154,12 @@ dependencies = [
"siphasher",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@ -1089,6 +1258,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
@ -1119,6 +1294,26 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@ -1157,6 +1352,21 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rgb"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
dependencies = [
"bytemuck",
]
[[package]]
name = "roff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]]
name = "rstml"
version = "0.11.2"
@ -1171,6 +1381,27 @@ dependencies = [
"thiserror",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@ -1192,6 +1423,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.203"
@ -1247,6 +1484,30 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha256"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0"
dependencies = [
"async-trait",
"bytes",
"hex",
"sha2",
"tokio",
]
[[package]]
name = "siphasher"
version = "0.3.11"
@ -1271,9 +1532,11 @@ dependencies = [
"notify-debouncer-mini",
"npezza93-tree-sitter-nix",
"once_cell",
"oxipng",
"pulldown-cmark",
"regex",
"serde",
"sha256",
"tree-sitter",
"tree-sitter-css",
"tree-sitter-haskell",
@ -1359,6 +1622,12 @@ dependencies = [
"syn 2.0.53",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "thiserror"
version = "1.0.59"
@ -1403,6 +1672,17 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
dependencies = [
"backtrace",
"bytes",
"pin-project-lite",
]
[[package]]
name = "tree-sitter"
version = "0.22.6"
@ -1919,6 +2199,15 @@ version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "yaml-rust2"
version = "0.8.1"

View file

@ -15,8 +15,10 @@ hayagriva = "0.5.3"
hypertext = "0.5.1"
katex = "0.4.6"
once_cell = "1.19.0"
oxipng = { version = "9.1.1", default-features = false, features = ["filetime", "parallel"] }
regex = "1.10.5"
serde = { version = "1.0.203", features = ["derive"] }
sha256 = "1.5.0"
# Watch
notify = "6.1.1"

View file

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io;
@ -5,7 +6,7 @@ use std::io::Write;
use std::path::Path;
use std::process::Command;
use camino::Utf8Path;
use camino::{Utf8Path, Utf8PathBuf};
use crate::pipeline::{AssetKind, Output, OutputKind, Sack, Virtual};
use crate::BuildContext;
@ -23,10 +24,21 @@ pub(crate) fn build_styles() {
fs::write("dist/styles.css", css).unwrap();
}
pub(crate) fn build_content(ctx: &BuildContext, pending: &[&Output], hole: &[&Output]) {
pub(crate) fn build_content(
ctx: &BuildContext,
pending: &[&Output],
hole: &[&Output],
hash: Option<HashMap<Utf8PathBuf, Utf8PathBuf>>,
) -> HashMap<Utf8PathBuf, Utf8PathBuf> {
let now = std::time::Instant::now();
render_all(ctx, pending, hole);
let hashes = render_all(ctx, pending, hole, hash);
println!("Elapsed: {:.2?}", now.elapsed());
copy_recursively(Path::new(".hash"), Path::new("dist/hash")).unwrap();
let mut lmao = HashMap::<Utf8PathBuf, Utf8PathBuf>::new();
for hash in hashes {
lmao.insert(hash.file, hash.hash);
}
lmao
}
pub(crate) fn build_static() {
@ -72,26 +84,64 @@ fn copy_recursively(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<
Ok(())
}
fn render_all(ctx: &BuildContext, pending: &[&Output], hole: &[&Output]) {
for item in pending {
let file = match &item.kind {
OutputKind::Asset(a) => Some(&a.meta.path),
OutputKind::Virtual(_) => None,
};
render(
item,
Sack {
ctx,
hole,
path: &item.path,
file,
},
);
}
fn render_all(
ctx: &BuildContext,
pending: &[&Output],
hole: &[&Output],
hash: Option<HashMap<Utf8PathBuf, Utf8PathBuf>>,
) -> Vec<Hashed> {
pending
.iter()
.filter_map(|item| {
let file = match &item.kind {
OutputKind::Asset(a) => Some(&a.meta.path),
OutputKind::Virtual(_) => None,
};
render(
item,
Sack {
ctx,
hole,
path: &item.path,
file,
hash: hash.clone(),
},
)
})
.collect()
}
fn render(item: &Output, sack: Sack) {
let o = Utf8Path::new("dist").join(&item.path);
fn store_hash_png(data: &[u8]) -> Utf8PathBuf {
let store = Utf8Path::new(".hash").join("png");
let hash = sha256::digest(data);
let hash_path = store.join(&hash).with_extension("png");
if !hash_path.exists() {
let opts = oxipng::Options {
interlace: Some(oxipng::Interlacing::Adam7),
..Default::default()
};
let data = oxipng::optimize_from_memory(data, &opts).expect("PNG Error");
fs::create_dir_all(&store).unwrap();
fs::write(hash_path, data).expect("Couldn't output optimized PNG");
}
Utf8Path::new("/")
.join("hash")
.join("png")
.join(hash)
.with_extension("png")
}
#[derive(Debug)]
pub(crate) struct Hashed {
pub file: Utf8PathBuf,
pub hash: Utf8PathBuf,
}
fn render(item: &Output, sack: Sack) -> Option<Hashed> {
let dist = Utf8Path::new("dist");
let o = dist.join(&item.path);
fs::create_dir_all(o.parent().unwrap()).unwrap();
match item.kind {
@ -103,19 +153,32 @@ fn render(item: &Output, sack: Sack) {
let mut file = File::create(&o).unwrap();
file.write_all(closure(&sack).as_bytes()).unwrap();
println!("HTML: {} -> {}", i, o);
None
}
AssetKind::Bibtex(_) => {}
AssetKind::Bibtex(_) => None,
AssetKind::Image => {
let hash = match item.path.extension() {
Some("png") => Some(store_hash_png(&std::fs::read(i).unwrap())),
Some("") => None,
_ => None,
};
fs::create_dir_all(o.parent().unwrap()).unwrap();
fs::copy(i, &o).unwrap();
println!("Image: {} -> {}", i, o);
hash.map(|hash| Hashed {
file: item.path.to_owned(),
hash,
})
}
};
}
}
OutputKind::Virtual(Virtual(ref closure)) => {
let mut file = File::create(&o).unwrap();
file.write_all(closure(&sack).as_bytes()).unwrap();
println!("Virtual: -> {}", o);
None
}
}
}

View file

@ -1,6 +1,9 @@
use std::collections::HashMap;
use hypertext::{html_elements, maud, maud_move, GlobalAttributes, Raw, Renderable};
use crate::{pipeline::Sack, text::md::parse, LinkDate, Linkable};
use crate::pipeline::Sack;
use crate::text::md::parse;
const INTRO: &str = r#"
##
@ -15,7 +18,7 @@ const INTRO: &str = r#"
"#;
fn intro() -> impl Renderable {
let (_, html, _) = parse(INTRO.into(), None);
let (_, html, _) = parse(INTRO.into(), None, "".into(), HashMap::new());
maud!(
section .p-card.intro-jp lang="ja-JP" {
(Raw(html))

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use camino::Utf8PathBuf;
use chrono::{DateTime, Utc};
use hayagriva::Library;
@ -18,8 +20,13 @@ pub(crate) struct Post {
}
impl Content for Post {
fn parse(data: String, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
crate::text::md::parse(data, lib)
fn parse(
data: String,
lib: Option<&Library>,
dir: Utf8PathBuf,
hash: HashMap<Utf8PathBuf, Utf8PathBuf>,
) -> (Outline, String, Option<Vec<String>>) {
crate::text::md::parse(data, lib, dir, hash)
}
fn render<'s, 'p, 'html>(

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use camino::Utf8PathBuf;
use chrono::{DateTime, Utc};
use hayagriva::Library;
@ -26,13 +28,18 @@ pub(crate) struct Slideshow {
}
impl Content for Slideshow {
fn parse(data: String, _: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
fn parse(
data: String,
_: Option<&Library>,
dir: Utf8PathBuf,
hash: HashMap<Utf8PathBuf, Utf8PathBuf>,
) -> (Outline, String, Option<Vec<String>>) {
let html = data
.split("\n-----\n")
.map(|chunk| {
chunk
.split("\n---\n")
.map(|s| crate::text::md::parse(s.to_owned(), None))
.map(|s| crate::text::md::parse(s.to_owned(), None, dir.clone(), hash.clone()))
.map(|e| e.1)
.collect::<Vec<_>>()
})

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use camino::Utf8PathBuf;
use hayagriva::Library;
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
@ -14,8 +16,13 @@ pub struct Wiki {
}
impl Content for Wiki {
fn parse(data: String, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
crate::text::md::parse(data, lib)
fn parse(
data: String,
lib: Option<&Library>,
path: Utf8PathBuf,
hash: HashMap<Utf8PathBuf, Utf8PathBuf>,
) -> (Outline, String, Option<Vec<String>>) {
crate::text::md::parse(data, lib, path, hash)
}
fn render<'s, 'p, 'html>(

View file

@ -6,7 +6,7 @@ mod ts;
mod utils;
mod watch;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::process::Command;
@ -120,7 +120,7 @@ fn main() {
kind: Asset {
kind: pipeline::AssetKind::html(|sack| {
let data = std::fs::read_to_string("content/index.md").unwrap();
let (_, html, _) = text::md::parse(data, None);
let (_, html, _) = text::md::parse(data, None, "".into(), HashMap::new());
crate::html::home(sack, Raw(html))
.render()
.to_owned()
@ -210,7 +210,8 @@ fn build(ctx: &BuildContext, sources: &[Source], special: Vec<Output>) -> Vec<Ou
let assets: Vec<_> = sources.iter().chain(special.iter()).collect();
crate::build::build_content(ctx, &assets, &assets);
let lmao = crate::build::build_content(ctx, &assets, &assets, None);
crate::build::build_content(ctx, &assets, &assets, Some(lmao));
crate::build::build_static();
crate::build::build_styles();
crate::build::build_pagefind();
@ -254,13 +255,18 @@ where
Some("md" | "mdx" | "lhs") => {
let raw = fs::read_to_string(&meta.path).unwrap();
let (matter, parsed) = parse_frontmatter::<T>(&raw);
let link = T::as_link(&matter, Utf8Path::new("/").join(dir));
let link = T::as_link(&matter, Utf8Path::new("/").join(&dir));
Output {
kind: Asset {
kind: pipeline::AssetKind::html(move |sack| {
let lib = sack.get_library();
let (outline, parsed, bib) = T::parse(parsed.clone(), lib);
let (outline, parsed, bib) = T::parse(
parsed.clone(),
lib,
dir.clone(),
sack.hash.as_ref().map(ToOwned::to_owned).unwrap_or_default(),
);
T::render(matter.clone(), sack, Raw(parsed), outline, bib)
.render()
.into()

View file

@ -20,8 +20,12 @@ use crate::{BuildContext, Link, LinkDate, Linkable};
pub(crate) trait Content {
/// Parse the document. Pass an optional library for bibliography.
/// This generates the initial HTML markup from content.
fn parse(document: String, library: Option<&Library>)
-> (Outline, String, Option<Vec<String>>);
fn parse(
document: String,
library: Option<&Library>,
path: Utf8PathBuf,
hash: HashMap<Utf8PathBuf, Utf8PathBuf>,
) -> (Outline, String, Option<Vec<String>>);
/// Render the full page from parsed content.
fn render<'s, 'p, 'html>(
@ -75,7 +79,7 @@ impl Debug for AssetKind {
// rust mental gymnastics moment
let ptr = &**fun as *const dyn Fn(&Sack) -> String as *const () as usize;
f.debug_tuple("Html").field(&ptr).finish()
},
}
Self::Bibtex(b) => f.debug_tuple("Bibtex").field(b).finish(),
Self::Image => write!(f, "Image"),
}
@ -188,6 +192,8 @@ pub(crate) struct Sack<'a> {
pub path: &'a Utf8PathBuf,
/// Original file location for this page
pub file: Option<&'a Utf8PathBuf>,
/// Hashed optimized images
pub hash: Option<HashMap<Utf8PathBuf, Utf8PathBuf>>
}
impl<'a> Sack<'a> {

View file

@ -1,5 +1,6 @@
use std::collections::HashMap;
use camino::{Utf8Path, Utf8PathBuf};
use hayagriva::{
archive::ArchivedStyle,
citationberg::{IndependentStyle, Locale, Style},
@ -47,7 +48,12 @@ static STYLE: Lazy<IndependentStyle> =
pub struct Outline(pub Vec<(String, String)>);
pub fn parse(text: String, lib: Option<&Library>) -> (Outline, String, Option<Vec<String>>) {
pub fn parse(
text: String,
lib: Option<&Library>,
dir: Utf8PathBuf,
hash: HashMap<Utf8PathBuf, Utf8PathBuf>,
) -> (Outline, String, Option<Vec<String>>) {
let (outline, stream) = {
let stream = Parser::new_ext(&text, *OPTS);
let mut stream: Vec<_> = TextMergeStream::new(stream).collect();
@ -59,6 +65,7 @@ pub fn parse(text: String, lib: Option<&Library>) -> (Outline, String, Option<Ve
.into_iter()
.map(make_math)
.map(make_emoji)
.map(swap_hashed_image(dir, hash))
.collect::<Vec<_>>();
let stream = make_code(stream)
@ -349,3 +356,30 @@ fn make_emoji(event: Event) -> Event {
_ => event,
}
}
fn swap_hashed_image(
dir: Utf8PathBuf,
hash: HashMap<Utf8PathBuf, Utf8PathBuf>,
) -> impl Fn(Event) -> Event {
move |event| match event {
Event::Start(start) => match start {
Tag::Image {
dest_url,
link_type,
title,
id,
} => {
let rel = dir.join(dest_url.as_ref());
let hashed = hash.get(&rel).map(|path| path.as_str().to_owned().into());
Event::Start(Tag::Image {
link_type,
dest_url: hashed.unwrap_or(dest_url),
title,
id,
})
}
_ => Event::Start(start),
},
_ => event,
}
}

View file

@ -6,7 +6,7 @@ use std::borrow::Cow;
use hypertext::{html_elements, maud_move, GlobalAttributes, Raw, Renderable};
use tree_sitter_highlight::{HighlightEvent, Highlighter};
pub enum Event {
pub enum TSEvent {
Write(String),
Enter(String),
Close,
@ -31,23 +31,23 @@ fn to_html(lang: &str, code: &str) -> String {
get_events(lang, code)
.into_iter()
.map(|event| match event {
Event::Write(text) => Cow::from(
TSEvent::Write(text) => Cow::from(
text.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;"),
),
Event::Enter(class) => {
TSEvent::Enter(class) => {
Cow::from(format!("<span class=\"{}\">", class.replace('.', "-")))
}
Event::Close => Cow::from("</span>"),
TSEvent::Close => Cow::from("</span>"),
})
.collect()
}
fn get_events(lang: &str, src: &str) -> Vec<Event> {
fn get_events(lang: &str, src: &str) -> Vec<TSEvent> {
let config = match configs::get_config(lang) {
Some(c) => c,
None => return vec![Event::Write(src.into())],
None => return vec![TSEvent::Write(src.into())],
};
let mut hl = Highlighter::new();
@ -66,10 +66,10 @@ fn get_events(lang: &str, src: &str) -> Vec<Event> {
out
}
fn map_event(event: HighlightEvent, src: &str) -> Event {
fn map_event(event: HighlightEvent, src: &str) -> TSEvent {
match event {
HighlightEvent::Source { start, end } => Event::Write(src[start..end].into()),
HighlightEvent::HighlightStart(s) => Event::Enter(captures::NAMES[s.0].into()),
HighlightEvent::HighlightEnd => Event::Close,
HighlightEvent::Source { start, end } => TSEvent::Write(src[start..end].into()),
HighlightEvent::HighlightStart(s) => TSEvent::Enter(captures::NAMES[s.0].into()),
HighlightEvent::HighlightEnd => TSEvent::Close,
}
}

View file

@ -118,7 +118,7 @@ pub fn watch(ctx: &BuildContext, sources: &[Source], state: Vec<Output>) -> Resu
let state_next = update_stream(&state, &items);
let abc: Vec<&Output> = items.iter().map(AsRef::as_ref).collect();
let xyz: Vec<&Output> = state_next.iter().map(AsRef::as_ref).collect();
build_content(ctx, &abc, &xyz);
build_content(ctx, &abc, &xyz, None);
state = state_next;
dirty = true;
}

View file

@ -1,6 +1,5 @@
@use '../consts' as consts;
.l-home {
display: flex;
flex-direction: column;