redvault-ai/darm_test/scripts/build_css.rs

139 lines
5.1 KiB
Rust
Executable file

#!/usr/bin/env rust-script
//! This is a regular crate doc comment, but it also contains a partial
//! Cargo manifest. Note the use of a *fenced* code block, and the
//! `cargo` "language".
//!
//! ```cargo
//! [dependencies]
//! encre-css = "0.14"
//! walkdir = "2"
//! grass = { version = "0.13.4", features = ["macro", "nightly"] }
//! lightningcss = { version = "1.0.0-alpha.65", features = ["bundler", "browserslist"] }
//! anyhow = "1"
//! async-compression = { version = "0.4", features = ["tokio", "zstd"] }
//! tokio = { version = "1", features = [ "full" ] }
//! ```
use std::{collections::BTreeSet, fs, path::Path};
use async_compression::tokio::write::ZstdEncoder;
use encre_css::{Config, Scanner};
use lightningcss::{
bundler::{Bundler, FileProvider},
stylesheet::{ParserOptions, PrinterOptions},
targets::{Browsers, Targets},
};
use tokio::io::AsyncWriteExt as _; // for `write_all` and `shutdown`
use walkdir::WalkDir;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
eprintln!("WEGL CSS 💅 © 2025==========================================");
eprintln!("look mum no js/npm");
eprintln!("Reading! Step 1/5 🚶 (Walking Dirs)");
// Walk through the directory structure
let contents = WalkDir::new("./")
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| {
e.file_type().is_file()
&& e.path()
.extension()
.map_or(false, |ext| ["rs", "scss"].into_iter().any(|e| e == ext))
})
.map(|entry| {
let path = entry.path();
// eprintln!("Processing file: {}", path.display());
let content = fs::read_to_string(path).expect("Failed to read");
let bx = Box::new(content);
let static_ref: &'static str = Box::leak(bx);
static_ref
});
let config = {
let mut config = Config::default();
// FIXME TODO Add moar colors
config.scanner = Scanner::from_fn(|val| {
let mut is_arbitrary = false;
val.split(|ch| {
// Escape all characters in arbitrary values prefixed by a dash (used to avoid
// ignoring values in, for example, JS arrays, given that they are defined
// using square brackets)
match ch {
'[' => {
is_arbitrary = true;
false
}
']' => {
is_arbitrary = false;
false
}
_ => {
ch == ' '
|| (!is_arbitrary
&& ['.', '\'', '"', '`', '\n'].into_iter().any(|c| c == ch))
}
}
})
.collect::<BTreeSet<&str>>()
});
// config
// .theme
// .colors
// .add("blue-500", "oklch(.623 .214 259.815)");
// config.theme.colors.add("blue", "rgb(.1, .1, .9)");
config
};
eprintln!("Building Utilities Step 2/5 ⚙️ (EncrCSS)");
let generated = encre_css::generate(contents, &config);
tokio::fs::write("./public/css/encr.css", &generated).await?;
tokio::fs::write("./style/encr.css", &generated).await?;
eprintln!("Compiling SCSS Step 3/5 🏗 (Grass Compile)");
let compiled = grass::from_path("./style/main.scss", &grass::Options::default())?;
tokio::fs::write("./public/css/main.css", compiled.clone()).await?;
eprintln!("Bundling & Minifying Step 4/5 ⚡️ (Lightning CSS)");
let fs: FileProvider = FileProvider::new();
let fs: &'static _ = Box::leak(Box::new(fs)); // yolo it's a script, and we need a 'static ref
let mut bundler = Bundler::new(fs, None, ParserOptions::default());
let stylesheet = bundler.bundle(Path::new("./public/css/main.css"))?;
// Serialize it to a string.
let optimized_opts = PrinterOptions {
minify: true,
targets: Targets {
browsers: Browsers::from_browserslist([
"> 0.5%, last 3 versions, Firefox ESR, not dead"
])?,
..Default::default()
},
..Default::default()
};
let res = stylesheet.to_css(Default::default()).unwrap();
let opt_res = stylesheet.to_css(optimized_opts).unwrap();
// println!("{}", res.code);
tokio::fs::write("./public/main.css", res.code.clone()).await?;
tokio::fs::write("./public/main.min.css", opt_res.code).await?;
eprintln!("Compressing... Step 5/5 🐁️ (Zstd)");
async fn compress(in_data: &[u8]) -> anyhow::Result<Vec<u8>> {
let mut encoder = ZstdEncoder::new(Vec::new());
encoder.write_all(in_data).await?;
encoder.shutdown().await?;
Ok(encoder.into_inner())
}
// tokio::fs::write(
// "./public/main.min.css.zstd",
// compress(res.code.as_bytes()).await?,
// )
// .await?;
eprintln!("Done ! =====================================================");
Ok(())
}