From f3db6c103884201033295307bd19f7ad76a467b0 Mon Sep 17 00:00:00 2001 From: yiffyrusdev Date: Thu, 8 May 2025 19:42:06 +0500 Subject: [PATCH] breaking: gate 'styly!(const scope...' behind the const-scoping feature.\nFor some reason it did only become a problem recently, but it appears that having lightningcss/grass unconditionally will break WASM builds by having getrandom dependency --- Cargo.toml | 25 +++++++++ README.md | 4 +- examples/leptos-components/Cargo.toml | 16 +++--- sabry_build/Cargo.toml | 16 +++--- sabry_intrnl/Cargo.toml | 31 +++++++---- sabry_intrnl/src/compiler/mod.rs | 27 +++++----- sabry_intrnl/src/config/mod.rs | 1 + sabry_intrnl/src/scoper/hash.rs | 13 +++-- sabry_intrnl/src/scoper/mod.rs | 16 +++--- sabry_intrnl/src/syntax/ostrta.rs | 2 + sabry_procmacro/Cargo.toml | 5 +- sabry_procmacro_impl/Cargo.toml | 17 +++--- sabry_procmacro_impl/src/impls/scssy.rs | 2 +- sabry_procmacro_impl/src/impls/styly.rs | 72 ++++++++++++++----------- 14 files changed, 150 insertions(+), 97 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 595e861..8742652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,33 @@ members = ["sabry_procmacro", "sabry_procmacro_impl", "sabry_intrnl"] exclude = ["examples"] [workspace.dependencies] +## Workspace sabry_intrnl = { path = "sabry_intrnl", version = "0.0.5" } sabry_procmacro = { path = "sabry_procmacro", version = "0.0.5" } sabry_procmacro_impl = { path = "sabry_procmacro_impl", version = "0.0.5" } sabry_build = { path = "sabry_build", version = "0.0.5" } +## Package +## Package +lightningcss.version = "1.0.0-alpha.65" +lightningcss.default-features = false +grass.version = "^0.13" +grass.default-features = false +raffia.version = "^0.9" +#/ +serde.version = "^1" +toml.version = "^0.8" +blake3.version = "^1" +base64.version = "^0.22" +regex.version = "^1" +#/ +itertools.version = "^0.14" +walkdir.version = "^2" +#/ +cfg-if.version = "^1" +thiserror.version = "^2" +proc-macro2.version = "^1" +quote.version = "^1" +syn.version = "^2" [workspace.package] version = "0.0.5" @@ -31,6 +54,7 @@ license.workspace = true publish.workspace = true [dependencies] +## Workspace sabry_procmacro = { workspace = true, optional = true } sabry_build = { workspace = true, optional = true } sabry_intrnl = { workspace = true } @@ -42,6 +66,7 @@ insta = { version = "^1.41", features = ["glob"] } [features] default = ["procmacro"] lepty-scoping = ["sabry_intrnl/lepty-scoping"] +const-scoping = ["sabry_procmacro?/const-compile"] procmacro = ["dep:sabry_procmacro"] build = ["dep:sabry_build"] net-files = ["sabry_build/net-files"] diff --git a/README.md b/README.md index 443dbec..857ad5e 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,6 @@ > > "master" branch is what's currently on crates.io > -> each version ever been on crates.io (except the latest release) is available in "vX.X.X" branch -> -> the latest-trashy state is in the "window" branch [![Crates.io](https://img.shields.io/crates/v/sabry.svg)](https://crates.io/crates/sabry) [![Docs.rs](https://img.shields.io/docsrs/sabry/latest.svg)](https://docs.rs/sabry) @@ -586,6 +583,7 @@ As you've seen above, scope doe not contain any style code by itself. That's the However you could still compile styles into the artifact by simply adding the `const` to the macro call: ```rust +// Requires `const-scoping` feature sabry::styly!(const scope "tests/assets/mixin-module.scss"); ``` Which results in following: diff --git a/examples/leptos-components/Cargo.toml b/examples/leptos-components/Cargo.toml index 4b9f858..9e8b6d6 100644 --- a/examples/leptos-components/Cargo.toml +++ b/examples/leptos-components/Cargo.toml @@ -13,19 +13,19 @@ leptos = { version = "^0.7" } leptos_meta = { version = "^0.7" } leptos_router = { version = "^0.7" } -sabry = {path = "../../", features = ["lepty-scoping"]} +sabry = { path = "../../", features = ["lepty-scoping", "const-scoping"] } [build-dependencies] -sabry = {path = "../../", features = ["build", "lepty-scoping"]} -define-styles = {path = "../define-styles"} +sabry = { path = "../../", features = [ + "build", + "lepty-scoping", + "const-scoping", +] } +define-styles = { path = "../define-styles" } [features] hydrate = ["leptos/hydrate"] -ssr = [ - "leptos/ssr", - "leptos_meta/ssr", - "leptos_router/ssr", -] +ssr = ["leptos/ssr", "leptos_meta/ssr", "leptos_router/ssr"] [package.metadata.sabry] css.minify = true diff --git a/sabry_build/Cargo.toml b/sabry_build/Cargo.toml index ce81bbc..dfa13e8 100644 --- a/sabry_build/Cargo.toml +++ b/sabry_build/Cargo.toml @@ -9,13 +9,17 @@ authors.workspace = true license.workspace = true [dependencies] -sabry_intrnl = { workspace = true } -sabry_procmacro_impl = { workspace = true } +sabry_intrnl.workspace = true +sabry_intrnl.features = ["compile", "scope"] +sabry_procmacro_impl.workspace = true +sabry_procmacro_impl.features = ["const-compile"] -syn = { version = "^2", features = ["visit", "derive", "parsing", "full"] } -proc-macro2 = { version = "^1", features = ["span-locations"] } -thiserror = { version = "^2" } -walkdir = { version = "^2" } +syn.workspace = true +syn.features = ["visit", "derive", "parsing", "full"] +proc-macro2.workspace = true +proc-macro2.features = ["span-locations"] +thiserror.workspace = true +walkdir.workspace = true [features] net-files = [] diff --git a/sabry_intrnl/Cargo.toml b/sabry_intrnl/Cargo.toml index 85d7a9f..b39c615 100644 --- a/sabry_intrnl/Cargo.toml +++ b/sabry_intrnl/Cargo.toml @@ -9,21 +9,30 @@ authors.workspace = true license.workspace = true [dependencies] -syn = { version = "^2" } -raffia = { version = "^0.9" } -regex = { version = "^1" } -grass = { version = "^0.13" } -lightningcss = { version = "1.0.0-alpha.65" } -blake3 = { version = "^1.5" } -base64 = { version = "^0.22" } +syn.workspace = true +raffia.workspace = true +regex.workspace = true +regex.optional = true +grass.workspace = true +grass.optional = true +lightningcss.workspace = true +lightningcss.features = ["grid"] +lightningcss.optional = true +blake3.workspace = true +blake3.optional = true +base64.workspace = true +base64.optional = true -thiserror = { version = "^2" } -serde = { version = "^1", features = ["derive"] } -toml = { version = "^0.8" } -cfg-if = { version = "^1" } +thiserror.workspace = true +serde.workspace = true +serde.features = ["derive"] +toml.workspace = true +cfg-if.workspace = true [features] lepty-scoping = [] +scope = ["dep:blake3", "dep:base64", "dep:regex"] +compile = ["dep:lightningcss", "dep:grass"] [package.metadata.docs.rs] all-features = true diff --git a/sabry_intrnl/src/compiler/mod.rs b/sabry_intrnl/src/compiler/mod.rs index 374d527..adc398f 100644 --- a/sabry_intrnl/src/compiler/mod.rs +++ b/sabry_intrnl/src/compiler/mod.rs @@ -1,16 +1,8 @@ -use std::fmt::Debug; - -use lightningcss::{ - error::{MinifyErrorKind, PrinterErrorKind}, - printer::PrinterOptions, - stylesheet::MinifyOptions, - targets::Targets, -}; - -use crate::{config::SabryConfig, syntax::ostrta::OneSyntaxToRuleThemAll}; +use crate::config::SabryConfig; /// Convenience wrapper on Grass and Lightningcss pub struct CompilerAdapter { + #[allow(dead_code)] // without 'compile' feature, still want to set this config: SabryConfig, } @@ -21,9 +13,10 @@ impl CompilerAdapter { /// Compile given SASS/SCSS into CSS /// with respect to self.config + #[cfg(feature = "compile")] pub fn compile_module( &self, - syntax: OneSyntaxToRuleThemAll, + syntax: crate::syntax::ostrta::OneSyntaxToRuleThemAll, code: &str, ) -> Result { let options = grass::Options::from(&self.config).input_syntax(syntax.into()); @@ -35,7 +28,10 @@ impl CompilerAdapter { /// Perform lightningcss transformations on given css /// with respect to self.config.css.minify on minification + #[cfg(feature = "compile")] pub fn lightningcss(&self, css: &str) -> Result { + use lightningcss::{printer::PrinterOptions, stylesheet::MinifyOptions, targets::Targets}; + let mut lightsheet = match lightningcss::stylesheet::StyleSheet::parse( css, lightningcss::stylesheet::ParserOptions::default(), @@ -81,6 +77,7 @@ impl CompilerAdapter { } } +#[cfg(feature = "compile")] impl From<&SabryConfig> for grass::Options<'_> { fn from(config: &SabryConfig) -> Self { let mut options = grass::Options::default(); @@ -95,14 +92,15 @@ impl From<&SabryConfig> for grass::Options<'_> { } } +#[cfg(feature = "compile")] #[derive(thiserror::Error)] pub enum SabryCompilerError { #[error("Could not compile into CSS with grass")] GrassCompile(#[from] Box), #[error("Could not print compiled CSS with lightningcss")] - LightPrint(#[from] lightningcss::error::Error), + LightPrint(#[from] lightningcss::error::Error), #[error("Could not minify CSS with lightningcss")] - LightMinify(#[from] lightningcss::error::Error), + LightMinify(#[from] lightningcss::error::Error), #[error("Could not parse CSS with lightningcss")] LightParse { kind: String, @@ -110,7 +108,8 @@ pub enum SabryCompilerError { }, } -impl Debug for SabryCompilerError { +#[cfg(feature = "compile")] +impl std::fmt::Debug for SabryCompilerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let explain = match self { Self::LightPrint(e) => format!( diff --git a/sabry_intrnl/src/config/mod.rs b/sabry_intrnl/src/config/mod.rs index 7fdfa13..dfa13bc 100644 --- a/sabry_intrnl/src/config/mod.rs +++ b/sabry_intrnl/src/config/mod.rs @@ -199,6 +199,7 @@ impl SabryLightTargets { } } +#[cfg(feature = "compile")] impl From for lightningcss::targets::Browsers { fn from(value: SabryLightTargets) -> Self { Self { diff --git a/sabry_intrnl/src/scoper/hash.rs b/sabry_intrnl/src/scoper/hash.rs index 21a875c..df21766 100644 --- a/sabry_intrnl/src/scoper/hash.rs +++ b/sabry_intrnl/src/scoper/hash.rs @@ -1,15 +1,14 @@ -use base64::Engine; - -use crate::config::SabryHashConfig; - -use super::{apply_basic_rusty_member_gen_rules, ArbitraryScope}; - /// Convenience wrapper for String-being-a-hash #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ScopeHash(String); impl ScopeHash { - pub fn new(scope: &ArbitraryScope, config: &SabryHashConfig) -> Self { + #[cfg(feature = "scope")] + pub fn new(scope: &super::ArbitraryScope, config: &crate::config::SabryHashConfig) -> Self { + use base64::Engine; + + use crate::scoper::apply_basic_rusty_member_gen_rules; + let mut hasher = blake3::Hasher::new(); if config.use_scope_name { diff --git a/sabry_intrnl/src/scoper/mod.rs b/sabry_intrnl/src/scoper/mod.rs index 24696d3..408b417 100644 --- a/sabry_intrnl/src/scoper/mod.rs +++ b/sabry_intrnl/src/scoper/mod.rs @@ -3,12 +3,8 @@ use std::fmt::Debug; use cfg_if::cfg_if; use hash::ScopeHash; use raffia::{Span, Spanned}; -use regex::Regex; -use crate::{ - config::SabryHashConfig, - syntax::{ostrta::OneSyntaxToRuleThemAll, StylesheetAdapter}, -}; +use crate::syntax::{ostrta::OneSyntaxToRuleThemAll, StylesheetAdapter}; pub mod hash; @@ -56,7 +52,11 @@ impl<'s> ArbitraryScope<'s> { /// Consume arbitrary scope and create a [HashedScope] in its basis /// /// This function calls [HashedScope::new] under the hood - pub fn hashed(self, config: &SabryHashConfig) -> Result, ScopeError> { + #[cfg(feature = "scope")] + pub fn hashed( + self, + config: &crate::config::SabryHashConfig, + ) -> Result, ScopeError> { let hash = ScopeHash::new(&self, config); HashedScope::new(hash, self) } @@ -332,6 +332,7 @@ impl ScopedSelector { } /// Generate rusty member ident based on selector type and CSS ident + #[cfg(feature = "scope")] pub fn gen_rusty_ident(&self) -> Option { let arb = &self.as_arbitrary().ident; let basic = apply_basic_rusty_member_gen_rules(arb); @@ -401,8 +402,9 @@ impl ScopedSelector { } } +#[cfg(feature = "scope")] pub fn apply_basic_rusty_member_gen_rules(source: &str) -> String { - let omit_regex = Regex::new(r"(^\-)|(\-$)|[^a-zA-Z0-9\-\_]") + let omit_regex = regex::Regex::new(r"(^\-)|(\-$)|[^a-zA-Z0-9\-\_]") .expect("BUG: can not build omition regex for rusty member generation"); // omit forbidden chars diff --git a/sabry_intrnl/src/syntax/ostrta.rs b/sabry_intrnl/src/syntax/ostrta.rs index 6dd4f69..8d6826c 100644 --- a/sabry_intrnl/src/syntax/ostrta.rs +++ b/sabry_intrnl/src/syntax/ostrta.rs @@ -34,6 +34,7 @@ impl TryFrom for OneSyntaxToRuleThemAll { } } +#[cfg(feature = "compile")] impl TryFrom for OneSyntaxToRuleThemAll { type Error = (); fn try_from(value: grass::InputSyntax) -> Result { @@ -54,6 +55,7 @@ impl From for raffia::Syntax { } } +#[cfg(feature = "compile")] impl From for grass::InputSyntax { fn from(value: OneSyntaxToRuleThemAll) -> Self { match value { diff --git a/sabry_procmacro/Cargo.toml b/sabry_procmacro/Cargo.toml index fb95447..12758de 100644 --- a/sabry_procmacro/Cargo.toml +++ b/sabry_procmacro/Cargo.toml @@ -12,11 +12,12 @@ license.workspace = true proc-macro = true [dependencies] -sabry_procmacro_impl = { workspace = true } -cfg-if = { version = "^1" } +sabry_procmacro_impl.workspace = true +cfg-if.workspace = true [features] nightly = [] +const-compile = ["sabry_procmacro_impl/const-compile"] [package.metadata.docs.rs] all-features = true diff --git a/sabry_procmacro_impl/Cargo.toml b/sabry_procmacro_impl/Cargo.toml index 8e468f6..ae8189a 100644 --- a/sabry_procmacro_impl/Cargo.toml +++ b/sabry_procmacro_impl/Cargo.toml @@ -9,15 +9,20 @@ authors.workspace = true license.workspace = true [dependencies] -sabry_intrnl = { workspace = true } -syn = { version = "^2", features = ["parsing"] } -quote = { version = "^1" } -proc-macro2 = { version = "^1", features = ["span-locations"] } -regex = { version = "^1" } -itertools = { version = "^0.14" } +sabry_intrnl.workspace = true +sabry_intrnl.features = ["scope"] +syn.workspace = true +syn.features = ["parsing"] +quote.workspace = true +proc-macro2.workspace = true +proc-macro2.features = ["span-locations"] +regex.workspace = true +itertools.workspace = true +cfg-if.workspace = true [features] nightly = [] +const-compile = ["sabry_intrnl/compile"] [package.metadata.docs.rs] all-features = true diff --git a/sabry_procmacro_impl/src/impls/scssy.rs b/sabry_procmacro_impl/src/impls/scssy.rs index 0684b70..ddbee04 100644 --- a/sabry_procmacro_impl/src/impls/scssy.rs +++ b/sabry_procmacro_impl/src/impls/scssy.rs @@ -36,7 +36,7 @@ pub fn scssy_macro_impl(input: TokenStream, source_path: Option) -> Tok } let sourcesass = code.to_string(); - let macro_doc = format!("Arbitrary {:?} code declared with `scssy!`. Pretty usable in tandem with `usey!` and `buildy` at build time", syntax); + let macro_doc = format!("Arbitrary {syntax:?} code declared with `scssy!`. Pretty usable in tandem with `usey!` and `buildy` at build time"); quote! { #[doc = #macro_doc] diff --git a/sabry_procmacro_impl/src/impls/styly.rs b/sabry_procmacro_impl/src/impls/styly.rs index 7e9d68d..a0a7e23 100644 --- a/sabry_procmacro_impl/src/impls/styly.rs +++ b/sabry_procmacro_impl/src/impls/styly.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; +use cfg_if::cfg_if; use itertools::Itertools; use proc_macro2::{Span, TokenStream}; use quote::quote; use sabry_intrnl::{ - compiler::CompilerAdapter, config::SabryConfig, scoper::{apply_basic_rusty_member_gen_rules, ArbitraryScope, ScopedSelector}, }; @@ -38,10 +38,7 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok Err(e) => { return syn::Error::new( Span::call_site(), - format!( - "Could not read sabry configuration required by this macro: {:?}", - e - ), + format!("Could not read sabry configuration required by this macro: {e:?}",), ) .to_compile_error() } @@ -63,7 +60,7 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok Err(e) => { return syn::Error::new( Span::call_site(), - format!("Could not generate scope: {:?}", e), + format!("Could not generate scope: {e:?}"), ) .to_compile_error() } @@ -127,7 +124,7 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok }) }) .map(|(name, ident, html, css)| { - let docs = format!("'{:?}' {}. CSS selector '{}'", ident, name, css); + let docs = format!("'{ident:?}' {name}. CSS selector '{css}'"); quote! { #[doc = #docs] #[allow(non_upper_case_globals)] @@ -142,33 +139,44 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok let wrp_docs = format!("wrapper class for '{}' scope. If you have any tagname selectors - they should live as children of element with this class applied.", &scope_ident); if constant { - let compiler = CompilerAdapter::new(config.clone()); - let css = match compiler.compile_module(ms.syntax.0, &scope.hashed_code) { - Ok(c) => c, - Err(e) => { - return syn::Error::new( - ms.code.span, - format!("Could not compile const inline: {e:?}"), + cfg_if! { + if #[cfg(feature = "const-compile")] { + use sabry_intrnl::compiler::CompilerAdapter; + let compiler = CompilerAdapter::new(config.clone()); + let css = match compiler.compile_module(ms.syntax.0, &scope.hashed_code) { + Ok(c) => c, + Err(e) => { + return syn::Error::new( + ms.code.span, + format!("Could not compile const inline: {e:?}"), + ) + .to_compile_error() + } + }; + + let const_docs = format!("The compiled CSS style for {} scope", &scope_ident); + let const_wrapper_ident = syn::parse_str::( + format!("{}_CSS", &scope_ident.to_string().to_uppercase()).as_str(), ) - .to_compile_error() - } - }; + .expect("BUG: We just converted valid Ident to string and uppercased"); - let const_docs = format!("The compiled CSS style for {} scope", &scope_ident); - let const_wrapper_ident = syn::parse_str::( - format!("{}_CSS", &scope_ident.to_string().to_uppercase()).as_str(), - ) - .expect("BUG: We just converted valid Ident to string and uppercased"); - - quote! { - #[doc = #const_docs] - #scope_vis const #const_wrapper_ident: &str = #css ; - #[doc = #wrp_docs] - #scope_vis const #scope_wrapper_ident : &str = #scope_hash ; - #[doc = #mod_docs] - #scope_vis mod #scope_ident { - #(#scope_members)* - #(#special_nesting_members)* + quote! { + #[doc = #const_docs] + #scope_vis const #const_wrapper_ident: &str = #css ; + #[doc = #wrp_docs] + #scope_vis const #scope_wrapper_ident : &str = #scope_hash ; + #[doc = #mod_docs] + #scope_vis mod #scope_ident { + #(#scope_members)* + #(#special_nesting_members)* + } + } + } else { + return syn::Error::new( + Span::call_site(), + "`styly!(const scope...` requires `experimental-const-scoping` feature flag", + ) + .to_compile_error(); } } } else {