SCSS compilation
This commit is contained in:
parent
60858bde24
commit
5d795c3084
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -17,6 +17,18 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@ -26,6 +38,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@ -245,6 +263,12 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "codemap"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
@ -561,8 +585,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -595,11 +621,38 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grass"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7a68216437ef68f0738e48d6c7bb9e6e6a92237e001b03d838314b068f33c94"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"grass_compiler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grass_compiler"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6"
|
||||
dependencies = [
|
||||
"codemap",
|
||||
"indexmap",
|
||||
"lasso",
|
||||
"once_cell",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
@ -889,6 +942,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lasso"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@ -1156,6 +1218,7 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -1189,6 +1252,19 @@ dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.10.0"
|
||||
@ -1821,6 +1897,7 @@ dependencies = [
|
||||
"debounced",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"grass",
|
||||
"html5ever",
|
||||
"log",
|
||||
"markup5ever_rcdom",
|
||||
|
@ -23,6 +23,7 @@ compute_graph = { path = "crates/compute_graph" }
|
||||
debounced = "0.2.0"
|
||||
env_logger = "0.11.6"
|
||||
futures = "0.3.31"
|
||||
grass = { version = "0.13.4", default-features = false }
|
||||
html5ever = "0.27.0"
|
||||
log = "0.4.22"
|
||||
markup5ever_rcdom = "0.3.0"
|
||||
|
5
site_test/css/main.scss
Normal file
5
site_test/css/main.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.foo {
|
||||
.bar {
|
||||
color: red;
|
||||
}
|
||||
}
|
90
src/generator/css.rs
Normal file
90
src/generator/css.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::{cell::RefCell, collections::HashSet, io::Write, path::PathBuf, rc::Rc};
|
||||
|
||||
use compute_graph::{
|
||||
InvalidationSignal,
|
||||
builder::GraphBuilder,
|
||||
rule::{Input, InputVisitable, Rule},
|
||||
synchronicity::Asynchronous,
|
||||
};
|
||||
use grass::{Fs, Options, OutputStyle};
|
||||
use log::error;
|
||||
|
||||
use super::{
|
||||
FileWatcher,
|
||||
util::{content_path, output_writer},
|
||||
};
|
||||
|
||||
pub fn make_graph(
|
||||
builder: &mut GraphBuilder<(), Asynchronous>,
|
||||
watcher: Rc<RefCell<FileWatcher>>,
|
||||
) -> Input<()> {
|
||||
let invalidate_css_box = Rc::new(RefCell::new(None));
|
||||
let (css, invalidate_css) = builder.add_invalidatable_rule(CompileScss {
|
||||
watcher,
|
||||
watched: HashSet::new(),
|
||||
invalidate: Rc::clone(&invalidate_css_box),
|
||||
});
|
||||
invalidate_css_box.replace(Some(invalidate_css));
|
||||
css
|
||||
}
|
||||
|
||||
#[derive(InputVisitable)]
|
||||
struct CompileScss {
|
||||
watcher: Rc<RefCell<FileWatcher>>,
|
||||
watched: HashSet<PathBuf>,
|
||||
invalidate: Rc<RefCell<Option<InvalidationSignal>>>,
|
||||
}
|
||||
impl Rule for CompileScss {
|
||||
type Output = ();
|
||||
fn evaluate(&mut self) -> Self::Output {
|
||||
let read_files = RefCell::new(vec![]);
|
||||
let fs = TrackingFs(&read_files);
|
||||
let style = if cfg!(debug_assertions) {
|
||||
OutputStyle::Expanded
|
||||
} else {
|
||||
OutputStyle::Compressed
|
||||
};
|
||||
let options = Options::default().fs(&fs).style(style);
|
||||
let result = grass::from_path(content_path("css/main.scss"), &options);
|
||||
let mut watcher = self.watcher.borrow_mut();
|
||||
for file in read_files.take() {
|
||||
if !self.watched.contains(&file) {
|
||||
self.watched.insert(file.clone());
|
||||
let signal = self.invalidate.borrow().clone().unwrap();
|
||||
watcher.watch(file, move || signal.invalidate());
|
||||
}
|
||||
}
|
||||
match result {
|
||||
Ok(s) => {
|
||||
output_writer("css/main.css")
|
||||
.expect("css writer")
|
||||
.write_all(s.as_bytes())
|
||||
.expect("writing css");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error compiling sass: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TrackingFs<'a>(&'a RefCell<Vec<PathBuf>>);
|
||||
impl<'a> Fs for TrackingFs<'a> {
|
||||
fn is_file(&self, path: &std::path::Path) -> bool {
|
||||
path.is_file()
|
||||
}
|
||||
|
||||
fn is_dir(&self, path: &std::path::Path) -> bool {
|
||||
path.is_dir()
|
||||
}
|
||||
|
||||
fn read(&self, path: &std::path::Path) -> std::io::Result<Vec<u8>> {
|
||||
self.0.borrow_mut().push(path.to_owned());
|
||||
std::fs::read(path)
|
||||
}
|
||||
|
||||
fn canonicalize(&self, path: &std::path::Path) -> std::io::Result<PathBuf> {
|
||||
std::fs::canonicalize(path)
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ pub fn parse<'a>(s: &'a str) -> impl Iterator<Item = Event<'a>> {
|
||||
options.insert(Options::ENABLE_STRIKETHROUGH);
|
||||
options.insert(Options::ENABLE_SMART_PUNCTUATION);
|
||||
let parser = Parser::new_ext(s, options);
|
||||
// TODO: revisit which of these stages are necessary, remove unused (and url crate dep)
|
||||
let heading_anchors = heading_anchors::new(parser);
|
||||
let link_decorations = link_decorations::new(heading_anchors);
|
||||
// note backrefs need to come before defs, because the defs stage replaces the
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod archive;
|
||||
mod css;
|
||||
mod markdown;
|
||||
mod posts;
|
||||
mod tags;
|
||||
@ -41,30 +42,23 @@ fn make_graph(watcher: Rc<RefCell<FileWatcher>>) -> anyhow::Result<AsyncGraph<()
|
||||
&mut *watcher.borrow_mut(),
|
||||
);
|
||||
|
||||
let tag_output = tags::make_graph(
|
||||
let tags = tags::make_graph(
|
||||
&mut builder,
|
||||
posts,
|
||||
default_template,
|
||||
&mut *watcher.borrow_mut(),
|
||||
);
|
||||
|
||||
let css = css::make_graph(&mut builder, Rc::clone(&watcher));
|
||||
|
||||
let post_metadatas_voided = builder.add_rule(MapToVoid(post_metadatas));
|
||||
let output = Combine::make(&mut builder, &[
|
||||
void_outputs,
|
||||
archive,
|
||||
tag_output,
|
||||
tags,
|
||||
css,
|
||||
post_metadatas_voided,
|
||||
]);
|
||||
builder.set_existing_output(output);
|
||||
Ok(builder.build()?)
|
||||
}
|
||||
|
||||
// #[derive(InputVisitable)]
|
||||
// struct Output {
|
||||
// archive: Input<()>,
|
||||
// posts: Input<()>,
|
||||
// }
|
||||
// impl Rule for Output {
|
||||
// type Output = ();
|
||||
// fn evaluate(&mut self) -> Self::Output {}
|
||||
// }
|
||||
|
@ -1,6 +1,8 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use notify::{EventHandler, Watcher};
|
||||
@ -10,14 +12,12 @@ use crate::generator::util::content_base_path;
|
||||
|
||||
pub struct FileWatcher {
|
||||
handlers: HashMap<PathBuf, Box<dyn Fn() -> ()>>,
|
||||
watcher: Option<notify::RecommendedWatcher>,
|
||||
}
|
||||
|
||||
impl FileWatcher {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
handlers: HashMap::new(),
|
||||
watcher: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,20 +35,18 @@ impl FileWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) -> anyhow::Result<()> {
|
||||
assert!(self.watcher.is_none());
|
||||
pub async fn start(self_: Rc<RefCell<Self>>) -> anyhow::Result<()> {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<notify::Result<notify::Event>>();
|
||||
|
||||
let mut watcher = notify::recommended_watcher(AsyncEventHandler(tx))?;
|
||||
watcher.watch(&content_base_path(), notify::RecursiveMode::Recursive)?;
|
||||
self.watcher = Some(watcher);
|
||||
|
||||
let mut absolute_content_parent = content_base_path().canonicalize()?;
|
||||
absolute_content_parent.pop();
|
||||
|
||||
while let Some(result) = rx.recv().await {
|
||||
if let Ok(ev) = result {
|
||||
self.handle_event(ev, &absolute_content_parent);
|
||||
self_.borrow().handle_event(ev, &absolute_content_parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,15 +48,14 @@ async fn main() {
|
||||
if matches.contains_id("watch") {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<()>();
|
||||
|
||||
let mut watcher = watcher.borrow_mut();
|
||||
watcher.watch(content_base_path(), move || {
|
||||
watcher.borrow_mut().watch(content_base_path(), move || {
|
||||
tx.send(()).expect("sending regenerate signal");
|
||||
});
|
||||
|
||||
let mut debounced =
|
||||
debounced(UnboundedReceiverStream::new(rx), Duration::from_millis(500));
|
||||
debounced(UnboundedReceiverStream::new(rx), Duration::from_millis(100));
|
||||
|
||||
let watch = watcher.start().fuse();
|
||||
let watch = FileWatcher::start(watcher).fuse();
|
||||
|
||||
let regenerate = async move {
|
||||
while let Some(_) = debounced.next().await {
|
||||
|
Loading…
x
Reference in New Issue
Block a user