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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
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]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
@ -26,6 +38,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -245,6 +263,12 @@ version = "0.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codemap"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -561,8 +585,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -595,11 +621,38 @@ dependencies = [
|
|||||||
"walkdir",
|
"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]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html5ever"
|
name = "html5ever"
|
||||||
@ -889,6 +942,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lasso"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -1156,6 +1218,7 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1189,6 +1252,19 @@ dependencies = [
|
|||||||
"rand",
|
"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]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@ -1821,6 +1897,7 @@ dependencies = [
|
|||||||
"debounced",
|
"debounced",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
|
"grass",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"log",
|
"log",
|
||||||
"markup5ever_rcdom",
|
"markup5ever_rcdom",
|
||||||
|
@ -23,6 +23,7 @@ compute_graph = { path = "crates/compute_graph" }
|
|||||||
debounced = "0.2.0"
|
debounced = "0.2.0"
|
||||||
env_logger = "0.11.6"
|
env_logger = "0.11.6"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
|
grass = { version = "0.13.4", default-features = false }
|
||||||
html5ever = "0.27.0"
|
html5ever = "0.27.0"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
markup5ever_rcdom = "0.3.0"
|
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_STRIKETHROUGH);
|
||||||
options.insert(Options::ENABLE_SMART_PUNCTUATION);
|
options.insert(Options::ENABLE_SMART_PUNCTUATION);
|
||||||
let parser = Parser::new_ext(s, options);
|
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 heading_anchors = heading_anchors::new(parser);
|
||||||
let link_decorations = link_decorations::new(heading_anchors);
|
let link_decorations = link_decorations::new(heading_anchors);
|
||||||
// note backrefs need to come before defs, because the defs stage replaces the
|
// note backrefs need to come before defs, because the defs stage replaces the
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mod archive;
|
mod archive;
|
||||||
|
mod css;
|
||||||
mod markdown;
|
mod markdown;
|
||||||
mod posts;
|
mod posts;
|
||||||
mod tags;
|
mod tags;
|
||||||
@ -41,30 +42,23 @@ fn make_graph(watcher: Rc<RefCell<FileWatcher>>) -> anyhow::Result<AsyncGraph<()
|
|||||||
&mut *watcher.borrow_mut(),
|
&mut *watcher.borrow_mut(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let tag_output = tags::make_graph(
|
let tags = tags::make_graph(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
posts,
|
posts,
|
||||||
default_template,
|
default_template,
|
||||||
&mut *watcher.borrow_mut(),
|
&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 post_metadatas_voided = builder.add_rule(MapToVoid(post_metadatas));
|
||||||
let output = Combine::make(&mut builder, &[
|
let output = Combine::make(&mut builder, &[
|
||||||
void_outputs,
|
void_outputs,
|
||||||
archive,
|
archive,
|
||||||
tag_output,
|
tags,
|
||||||
|
css,
|
||||||
post_metadatas_voided,
|
post_metadatas_voided,
|
||||||
]);
|
]);
|
||||||
builder.set_existing_output(output);
|
builder.set_existing_output(output);
|
||||||
Ok(builder.build()?)
|
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::{
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use notify::{EventHandler, Watcher};
|
use notify::{EventHandler, Watcher};
|
||||||
@ -10,14 +12,12 @@ use crate::generator::util::content_base_path;
|
|||||||
|
|
||||||
pub struct FileWatcher {
|
pub struct FileWatcher {
|
||||||
handlers: HashMap<PathBuf, Box<dyn Fn() -> ()>>,
|
handlers: HashMap<PathBuf, Box<dyn Fn() -> ()>>,
|
||||||
watcher: Option<notify::RecommendedWatcher>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileWatcher {
|
impl FileWatcher {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
handlers: HashMap::new(),
|
handlers: HashMap::new(),
|
||||||
watcher: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,20 +35,18 @@ impl FileWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(&mut self) -> anyhow::Result<()> {
|
pub async fn start(self_: Rc<RefCell<Self>>) -> anyhow::Result<()> {
|
||||||
assert!(self.watcher.is_none());
|
|
||||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<notify::Result<notify::Event>>();
|
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<notify::Result<notify::Event>>();
|
||||||
|
|
||||||
let mut watcher = notify::recommended_watcher(AsyncEventHandler(tx))?;
|
let mut watcher = notify::recommended_watcher(AsyncEventHandler(tx))?;
|
||||||
watcher.watch(&content_base_path(), notify::RecursiveMode::Recursive)?;
|
watcher.watch(&content_base_path(), notify::RecursiveMode::Recursive)?;
|
||||||
self.watcher = Some(watcher);
|
|
||||||
|
|
||||||
let mut absolute_content_parent = content_base_path().canonicalize()?;
|
let mut absolute_content_parent = content_base_path().canonicalize()?;
|
||||||
absolute_content_parent.pop();
|
absolute_content_parent.pop();
|
||||||
|
|
||||||
while let Some(result) = rx.recv().await {
|
while let Some(result) = rx.recv().await {
|
||||||
if let Ok(ev) = result {
|
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") {
|
if matches.contains_id("watch") {
|
||||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<()>();
|
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<()>();
|
||||||
|
|
||||||
let mut watcher = watcher.borrow_mut();
|
watcher.borrow_mut().watch(content_base_path(), move || {
|
||||||
watcher.watch(content_base_path(), move || {
|
|
||||||
tx.send(()).expect("sending regenerate signal");
|
tx.send(()).expect("sending regenerate signal");
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut debounced =
|
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 {
|
let regenerate = async move {
|
||||||
while let Some(_) = debounced.next().await {
|
while let Some(_) = debounced.next().await {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user