From dc3c3bac11b3c0f8e635247336ef2da1144c450d Mon Sep 17 00:00:00 2001 From: Yiffy Dev Date: Sat, 2 Nov 2024 01:33:28 +0500 Subject: [PATCH] feat!: introduce SASS prelude as well. Its more convenient then making build.rs sassy macro. --- README.md | 13 +++--- examples/leptos-axum/Cargo.toml | 2 + examples/leptos-axum/styles/app.scss | 5 +++ examples/leptos-axum/styles/prelude.css | 4 ++ sabry_build/src/buildmagic.rs | 56 ++++++++++++++++++++++--- sabry_intrnl/src/config/mod.rs | 6 ++- sabry_procmacro_impl/src/impls/mod.rs | 18 ++++++-- 7 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 examples/leptos-axum/styles/app.scss create mode 100644 examples/leptos-axum/styles/prelude.css diff --git a/README.md b/README.md index 9c398aa..25774c6 100644 --- a/README.md +++ b/README.md @@ -342,13 +342,14 @@ Full example, close to defaults: [package.metadata.sabry] css.bundle = "target/static/style.css" -css.bundle_prelude = ["assets/prelude.css"] +css.prelude = ["assets/prelude.css"] css.scopes = "target/statis/scopes/" css.minify = true sass.intermediate_dir = "target/.sabry/sass" sass.module_name_collision = "merge" sass.modules = ["assets/sass/mod1.scss"] +sass.prelude = ["assets/sass/prelude.scss"] sass.scanroot = "src" hash.size = 6 @@ -366,11 +367,11 @@ ie = "6" ### `sabry.css` -**bundle** *(no default)* - file path ro write CSS bundle into relative to project root +**bundle** *(no default)* - file path ro write CSS bundle into, relative to crate root -**bundle_prelude** *(no default)* - collection of CSS files, relative to the project root, which content will be inserted before the compiled style into the *bundle* file if any +**prelude** *(no default)* - collection of CSS files, relative to the crate root, which content will be inserted before the compiled style into the *bundle* file if any. Does not affect generated CSS scopes if any. -**scopes** *(no default)* - dir path to put separate CSS for every scope into relative to project root +**scopes** *(no default)* - dir path to put separate CSS for every scope into, relative to crate root **minify** *(default true)* - print compressed CSS output and do the lightningcss thing @@ -380,7 +381,9 @@ ie = "6" **scanroot** *(default "src")* - root directory to start scanning "rs" files from. Used in build function -**modules** *(no default)* - collection of SASS/SCSS files, relative to the project root, which should be available as modules as well +**modules** *(no default)* - collection of SASS/SCSS files, relative to the crate root, which should be available as modules as well + +**prelude** *(no default)* - collection of SASS/SCSS files, relative to the crate root, which content will be compiled into CSS and then inserted into the CSS *bundle* if any. Does not affect generated CSS scopes if any. **module_name_collision** *(default "merge")* - how to handle similary named modules. diff --git a/examples/leptos-axum/Cargo.toml b/examples/leptos-axum/Cargo.toml index 39e1e43..f1aadcd 100644 --- a/examples/leptos-axum/Cargo.toml +++ b/examples/leptos-axum/Cargo.toml @@ -49,6 +49,8 @@ ssr = [ css.minify = true # We'll use bundle for leptos css.bundle = "target/.sabry/target/bundle.css" +css.prelude = ["styles/prelude.css"] +sass.prelude = ["styles/app.scss"] [package.metadata.leptos] # Sabry-specific quality of life config diff --git a/examples/leptos-axum/styles/app.scss b/examples/leptos-axum/styles/app.scss new file mode 100644 index 0000000..9e4155f --- /dev/null +++ b/examples/leptos-axum/styles/app.scss @@ -0,0 +1,5 @@ +@use 'theme'; + +html { + @include theme.surface(primary); +} diff --git a/examples/leptos-axum/styles/prelude.css b/examples/leptos-axum/styles/prelude.css new file mode 100644 index 0000000..66e2b6f --- /dev/null +++ b/examples/leptos-axum/styles/prelude.css @@ -0,0 +1,4 @@ +html, body { + margin: 0; + padding: 0; +} diff --git a/sabry_build/src/buildmagic.rs b/sabry_build/src/buildmagic.rs index fe3043c..0e66c96 100644 --- a/sabry_build/src/buildmagic.rs +++ b/sabry_build/src/buildmagic.rs @@ -13,7 +13,7 @@ use sabry_intrnl::{ config::{manifest::ManifestError, BehavHashCollision, BehavSassModCollision, SabryConfig}, scoper::{hash::ScopeHash, ArbitraryScope, ScopeError}, }; -use sabry_procmacro_impl::impls::styly; +use sabry_procmacro_impl::impls::{styly, ArbitraryStyleSyntax}; use walkdir::WalkDir; use crate::filevisit::{self, FileVisitError}; @@ -164,6 +164,7 @@ impl SabryBuilder { println!("🧙 sabry didn't load any usable styles"); } + // compile styly! macro parsed styles for styly in &self.state.loaded_stylyses { let scope = ArbitraryScope::from_source( styly.syntax.into(), @@ -193,6 +194,12 @@ impl SabryBuilder { .push((scope.original_scope.name.to_string(), css)); } + // compile sass preludes into the CSS prelude + for pre in &self.state.sass_prelude { + let css = self.css_compiler.compile_module(pre.syntax.into(), &pre.code)?; + self.state.css_prelude.push_str(&css); + } + Ok(()) } @@ -230,12 +237,12 @@ impl SabryBuilder { Ok(()) } - /// Load up configured preludes as side modules + /// Load up configured preludes and side modules pub fn load_preludes(&mut self) -> BuilderResult { // load sass modules from config let mut modules: Vec = vec![]; - if let Some(sass_pre) = &self.config.sass.modules { - for pre in sass_pre { + if let Some(sass_mods) = &self.config.sass.modules { + for pre in sass_mods { let pre_path = PathBuf::from_str(pre)?; let pre_name = pre_path .file_name() @@ -250,8 +257,26 @@ impl SabryBuilder { self.load_side_module(name, code)?; } + // load SASS preludes + if let Some(sass_pres) = &self.config.sass.prelude { + let mut sass_preludes: Vec = vec![]; + for pre in sass_pres { + let pre_path = PathBuf::from_str(pre)?; + + let syntax = pre_path.extension().unwrap_or_default().to_str().unwrap_or_default(); + let syntax = match ArbitraryStyleSyntax::try_from(syntax) { + Ok(s) => s, + Err(_) => return Err(SabryBuildError::Another(format!("Unknown syntax for sass prelude {pre}"))) + }; + + let code = fs::read_to_string(pre_path)?; + sass_preludes.push(SassPreludeModule {syntax, code}); + } + self.state.sass_prelude.extend(sass_preludes); + } + // load css preludes - if let Some(css_pre) = &self.config.css.bundle_prelude { + if let Some(css_pre) = &self.config.css.prelude { for pre in css_pre { let code = fs::read_to_string(pre)?; self.state.css_prelude.push_str(&code); @@ -309,11 +334,30 @@ impl SabryBuilder { #[derive(Default)] pub struct SabryBuildState { + /// HashSet of scope hashes known by builder + /// Used to determine hash collision known_scope_hashes: HashSet, + /// HashSet of module names known by builder + /// Used to determine module name collision known_side_modules: HashSet, + /// styly! macro uses, parsed loaded_stylyses: Vec, + /// CSS modules to form bundle/write separately loaded_css_modules: Vec, + /// CSS prelude to write into bundle + /// Lives separately from [loaded_css_modules] to avoid name collision css_prelude: String, + /// SASS preludes loaded from config + /// Should be compiled into loaded_css_modules as well + sass_prelude: Vec +} + +/// Convenience struct for [SabryBuildState::sass_prelude] +pub struct SassPreludeModule { + /// syntax for the prelude + syntax: ArbitraryStyleSyntax, + /// code of the prelude + code: String } #[derive(Debug, thiserror::Error)] @@ -342,4 +386,6 @@ pub enum SabryBuildError { CssCompile(#[from] SabryCompilerError), #[error("Failed to load config/manifest")] Manifest(#[from] ManifestError), + #[error("Another error")] + Another(String) } diff --git a/sabry_intrnl/src/config/mod.rs b/sabry_intrnl/src/config/mod.rs index 0903929..7fdfa13 100644 --- a/sabry_intrnl/src/config/mod.rs +++ b/sabry_intrnl/src/config/mod.rs @@ -41,7 +41,7 @@ impl From<&ValuableManifest> for SabryConfig { pub struct SabryCssConfig { pub bundle: Option, pub scopes: Option, - pub bundle_prelude: Option>, + pub prelude: Option>, #[serde(default = "SabryCssConfig::default_minify")] pub minify: bool, } @@ -57,7 +57,7 @@ impl Default for SabryCssConfig { Self { bundle: None, scopes: None, - bundle_prelude: None, + prelude: None, minify: Self::default_minify(), } } @@ -71,6 +71,7 @@ pub struct SabrySassConfig { #[serde(default = "SabrySassConfig::default_scanroot")] pub scanroot: String, pub modules: Option>, + pub prelude: Option>, #[serde(default = "SabrySassConfig::default_intermediate_dir")] pub intermediate_dir: String, #[serde(default = "SabrySassConfig::default_module_name_collision")] @@ -94,6 +95,7 @@ impl Default for SabrySassConfig { Self { scanroot: Self::default_scanroot(), modules: None, + prelude: None, intermediate_dir: Self::default_intermediate_dir(), module_name_collision: Self::default_module_name_collision(), } diff --git a/sabry_procmacro_impl/src/impls/mod.rs b/sabry_procmacro_impl/src/impls/mod.rs index faa10df..0a0bbde 100644 --- a/sabry_procmacro_impl/src/impls/mod.rs +++ b/sabry_procmacro_impl/src/impls/mod.rs @@ -117,6 +117,17 @@ impl ToTokens for ArbitraryStyleSyntax { } } +impl TryFrom<&str> for ArbitraryStyleSyntax { + type Error = (); + fn try_from(value: &str) -> Result { + match value { + "sass" => Ok(Self::Sass), + "scss" => Ok(Self::Scss), + _ => Err(()) + } + } +} + impl Parse for ArbitraryStyleSyntax { fn parse(input: syn::parse::ParseStream) -> syn::Result { match input.parse::() { @@ -128,10 +139,9 @@ impl Parse for ArbitraryStyleSyntax { } let ident = input.parse::()?; - match ident.to_string().as_str() { - "sass" => Ok(Self::Sass), - "scss" => Ok(Self::Scss), - _ => Err(syn::Error::new( + match Self::try_from(ident.to_string().as_str()) { + Ok(v) => Ok(v), + Err(_) => Err(syn::Error::new( ident.span(), format!( "Available syntax are `sass` and `scss`, omit to use default {:?} syntax",