From ee1b401f4755ad6ae236c372aba0b465574970eb Mon Sep 17 00:00:00 2001 From: yiffyrusdev Date: Fri, 12 Sep 2025 14:15:47 +0500 Subject: [PATCH] refactor!: remove `lepty-scoping` feature and make its behaviour the default; deprecate scope members; bump MSRV; bump deps --- .github/workflows/cbt.yml | 64 ++++----- Cargo.toml | 16 +-- README.md | 178 +++++------------------- examples/define-styles/Cargo.toml | 4 +- examples/leptos-axum/Cargo.toml | 28 ++-- examples/leptos-components/Cargo.toml | 10 +- examples/use-styles/Cargo.toml | 10 +- sabry_intrnl/Cargo.toml | 2 - sabry_intrnl/src/config/mod.rs | 20 +-- sabry_intrnl/src/scoper/hash.rs | 7 +- sabry_intrnl/src/scoper/mod.rs | 142 ++++++++----------- sabry_intrnl/src/syntax/mod.rs | 124 +++++++++++------ sabry_intrnl/src/syntax/ostrta.rs | 11 +- sabry_procmacro_impl/Cargo.toml | 1 + sabry_procmacro_impl/src/impls/styly.rs | 37 ++++- 15 files changed, 282 insertions(+), 372 deletions(-) diff --git a/.github/workflows/cbt.yml b/.github/workflows/cbt.yml index ad3f3f1..97fdacb 100644 --- a/.github/workflows/cbt.yml +++ b/.github/workflows/cbt.yml @@ -1,40 +1,40 @@ name: cargo basic build-test on: - push: - branches: ["window", "master"] - pull_request: - branches: ["window"] + push: + branches: ["window", "master"] + pull_request: + branches: ["window"] env: - CARGO_TERM_COLOR: always + CARGO_TERM_COLOR: always jobs: - build_and_test: - name: Sabry - latest - runs-on: ubuntu-latest - strategy: - matrix: - toolchain: - - stable - steps: - - uses: actions/checkout@v4 - - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - - run: cargo build --verbose - - run: cargo test --verbose --all - - run: cargo test --verbose --all --features "const-scoping lepty-scoping" + build_and_test: + name: Sabry - latest + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - stable + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: cargo build --verbose + - run: cargo test --verbose --all + - run: cargo test --verbose --all --features "const-scoping" - build_examples: - name: Sabry - latset - examples - runs-on: ubuntu-latest - strategy: - matrix: - example: - - define-styles - - use-styles - - leptos-axum - steps: - - uses: actions/checkout@v4 - - run: rustup update nightly && rustup default nightly - - run: cd examples/${{ matrix.example }} - - run: cargo build --verbose + build_examples: + name: Sabry - latset - examples + runs-on: ubuntu-latest + strategy: + matrix: + example: + - define-styles + - use-styles + - leptos-axum + steps: + - uses: actions/checkout@v4 + - run: rustup update nightly && rustup default nightly + - run: cd examples/${{ matrix.example }} + - run: cargo build --verbose diff --git a/Cargo.toml b/Cargo.toml index 95acb73..d3fa278 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,16 +9,15 @@ sabry_intrnl = { path = "sabry_intrnl", version = "0.0.6-rc.1" } sabry_procmacro = { path = "sabry_procmacro", version = "0.0.6-rc.1" } sabry_procmacro_impl = { path = "sabry_procmacro_impl", version = "0.0.6-rc.1" } sabry_build = { path = "sabry_build", version = "0.0.6-rc.1" } -## Package -## Package -lightningcss.version = "1.0.0-alpha.65" +#/ +lightningcss.version = "1.0.0-alpha.67" lightningcss.default-features = false grass.version = "^0.13" grass.default-features = false -raffia.version = "^0.9" +raffia.version = "^0.11" #/ serde.version = "^1" -toml.version = "^0.8" +toml.version = "^0.9" blake3.version = "^1" base64.version = "^0.22" regex.version = "^1" @@ -35,7 +34,7 @@ syn.version = "^2" [workspace.package] version = "0.0.6-rc.1" edition = "2021" -rust-version = "1.78" +rust-version = "1.88" authors = ["Svyatoslav Pushkarev "] repository = "https://github.com/yiffyrusdev/sabry" license = "MIT" @@ -60,12 +59,11 @@ sabry_build = { workspace = true, optional = true } sabry_intrnl = { workspace = true } [dev-dependencies] -leptos = { version = "0.7.0-gamma3" } -insta = { version = "^1.41", features = ["glob"] } +leptos = { version = "^0.8" } +insta = { version = "^1", 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"] diff --git a/README.md b/README.md index 29b42b7..5c47c88 100644 --- a/README.md +++ b/README.md @@ -25,17 +25,6 @@ sabry::scssy!(tokens {"@mixin colored{color: red;}"});
-Get the *(almost)* proper autocompletion for your scope from rust-analyzer - -```rust -sabry::styly!(scope {".cls1{} #id1{}"}); -/* autocompleted for you */ -scope::cls1; -scope::theid1; -``` - -
- Code your SASS in separate files to get proper syntax highlighting, or do sass-in-the-rust to keep things in one place ```rust @@ -71,13 +60,23 @@ Keep things as private and modular as you wish ```rust sabry::styly!(pub cats "tests/assets/mixin-module.scss"); -sabry::styly!(dogs {"#howl {border: none;}"}); +sabry::styly!(dogs {".howl {border: none;}"}); ``` -```rust,ignore +```html /// something like render function { -
-
    +
    +
      +
      +} +``` +```html +/// or even better with leptos +view!{class=CATS +
      + {move || view!{class=DOGS, +
        + }}
        } ``` @@ -122,7 +121,7 @@ The only need is the dependency #Cargo.toml [dependencies] -sabry = {version = "0.0.5"} +sabry = {version = "0.0.6"} ``` And a proc-macro ```rust @@ -155,7 +154,7 @@ Depend on sabry # Cargo.toml [dependencies] -sabry = {version = "0.0.5"} +sabry = {version = "0.0.6"} ``` And create a style scope wherever you want: ```rust @@ -173,18 +172,18 @@ styly!(styles {" color: white; } "}); - -/* Now you can use all scoped selectors: */ -let badges_scoped_class = styles::badges; -let wolf_scoped_id = styles::thewolf; ``` -Every selector, if that does make sense, now available for you as a member of `styles` scope. In this example - `styles::badges`, `styles::thewolf` and `styles::_list()`. More about scoping and member names you can read [here](#styly-scopes). +```html +
          +
        • + "HOOOOWL!" +
        • +
        +``` ### Use styles earlier created in another crate -> **Tip** | If you use [leptos](https://github.com/leptos-rs/leptos) - check [this](#leptos-specials) section as well - The combination of previous two, with some additional work to do and some extra sugar to enjoy. Sabry is needed as both dependency and build-dependency. @@ -193,10 +192,10 @@ To be able to compile all styles sabry also needs the *build* feature flag: # Cargo.toml [dependencies] -sabry = {version = "0.0.5"} +sabry = {version = "0.0.6"} [build-dependencies] -sabry = {version = "0.0.5", features = ["build"]} +sabry = {version = "0.0.6", features = ["build"]} ``` > If you do use some non-default feature flags make sure to keep them in sync between sabry-dependency and sabry-build-dependency. @@ -318,81 +317,6 @@ So I'd say - do the const function back-propogation and let the consumer decide -### Leptos specials - -With [leptos](https://github.com/leptos-rs/leptos) framework you can do the trick to apply some class to most of HtmlElements for the entire component/island: - -```rust -use leptos::prelude::*; - -#[component] -fn component() -> impl IntoView { - view! {class="cls1", /* <- here */ -

        "Head"

        -

        "text"

        - } -} -``` -... and have "cls1" on both `h1` and `p` auto-assigned. Isn't that cool? - -So, if you're on leptos, and don't mind to take this approach, I'd highly recommend turning the *lepty-scoping* feature on: - -```toml -# Cargo.toml - -[dependencies] -sabry = {version = "0.0.5", features = ["lepty-scoping"]} - -[build-dependencies] -sabry = {version = "0.0.5", features = ["build", "lepty-scoping"]} -``` - -```rust -use leptos::prelude::*; -use sabry::styly; - -styly!(scope {" - // assuming we have tokens!() macro generated by `scssy!`, which is handled by `buildy` - @use 'tokens'; - h1 { - @include tokens.sectionhead(); - img { - @include tokens.sectionimg(); - } - } - .breadcumbs { - @include tokens.badgelist(); - &__item { - @include tokens.badge(secondary); - } - } -"}); - -#[component] -fn component() -> impl IntoView { - view! {class=SCOPE, -

        - "Head" - -

        -
          -
        • - "Home page" -
        • -
        - } -} -``` - -That will perform *much* better: - -- All scope members - like `scope::breadcumbs` - wont contain repeating scope hash, just the original class/id selector - - So instead of scope members (which [aren't](https://github.com/yiffyrusdev/sabry/issues/2) autocompleted for sass-in-the-rust yet) you could just write bare selectors, as you do, for example, with svelte: `
      • ` - - You'll still have the rusty mod generated: its just the `scope::breadcumbs` is the `"breadcumbs"` without the hash -- You will not encounter [the catch](#scoping) with nested tagname selectors - -Also, this isn't really exclusive leptos-supporting feature. It just changes scoping behavour. - ## Configuration Sabry configuration lives in `[package.metadata.sabry]` table of the manifest file. @@ -562,8 +486,8 @@ In general the scope does look like this: ```rust,ignore const FOX: &str = "J9k_s9"; mod fox { - pub const fur: &str = "J9k_s9 fur"; - pub fn _dark(c: &str) -> String {format!("{c}-dark")} + #[deprecated(since = "0.0.6", note = "will remove")] + pub const fur: &str = "fur J9k_s9"; } ``` @@ -572,9 +496,6 @@ mod fox { Styly macro itself does not generate the scope. It is done in the `sabry_intrnl::scoper`. However, as a result, you will have the following: - `const` with the UPPER_CASE name of the scope, which contains its hash -- `mod` with exact scope name which is the scope you're going to use - - for simple selectors, like `.fur` you will have a const members with hashed original selectors to use wherever you need a class/id/etc. - - for parent-selectors like `&-dark` you will have a non-const function, which will create matching the selector from any other. You can read more about scoping and hashing in the [scoping](#scoping) section. @@ -600,7 +521,6 @@ Which results in following: ```rust,ignore const SCOPE: &str = /* scope hash */; const SCOPE_CSS: &str = /* compiled from src/assets/scope.scss */; -mod scope {/* selector collection */} ``` > **There is a catch** | with the `const` modifier macro must compile CSS at compile-time. That results in several game changers: @@ -666,15 +586,15 @@ Exactly this kind of macros is produced by [`scssy!`](#style-definition-with-scs ### Scoping -Sabry handles scoping by restriction or mutation of existing selectors +Sabry handles scoping by restriction of existing selectors with the hash. Hash is calculated for the entire scope by the [`styly!`](#scoping-with-styly) macro. Currently the following types of selectors are scoped: - class - id +- tagname - SASS parent selectors (see below, there is a catch) -- tagname (see below, there is a catch) Sabry does not make difference between top-level and nested selectors, also selector complexity isn't taken into account: sabry simply walks through all compound selectors @@ -683,40 +603,8 @@ and apply scoping for supported ones. Different selector types are scoped differently: - class selectors are restricted with scope hash: `.class` -> `.HASH.class` -- id selector are mutated with scope hash: `#id` -> `#id-HASH` -- tagname selectors are changed into indirect descendance: `div` -> `.HASH div` - -> **There is a catch** | currently tagname selectors are scoped with indirect descendancy -> hash restriction. That means - if you do use tagnames in scoped stylesheets, you have to wrap your markdown -> with `SCOPE` class generated by `styly!` macro. -> -> Also if there are nested tagname selectors - sabry won't take this into account and you'll -> have to wrap them with `SCOPE` class as well -> -> **However** If you are able to apply some class to all html elements you have - like `view!{class=CLASS...}` with Leptos - you could use *lepty-scoping* feature flag for sabry and get rid of this catch! See more [here](#leptos-specials). - -As for **SASS parent selectors**: they are currently handled in different way. Instead of -walking up the syntax tree sabry just creates function member for the scope and leave the rest to grass: - -```rust -sabry::styly!(scope {" - .cls1 { - &-dark {} - } -"}); -``` -is something like -```rust,ignore -const SCOPE: &str = "Ut8CskJ"; -mod scope { - pub const cls1: &str = "Ut8CskJ cls1"; - pub fn _dark(c: &str) -> String {format!("{c}-dark")} -} -``` - -So you still can get the scoped variant of `cls1-dark` class: `scope::_dark(scope::cls1)`. - -This isn't very handy, also isn't strict enough, and is a high-priority subject to change. wip. +- id selector are mutated with scope hash: `#id` -> `.HASH#id` +- tagname selectors are changed into indirect descendance: `div` -> `div.HASH` #### Scope member naming rules @@ -763,9 +651,9 @@ If you'd like to lend a paw - feel free to check the [WIP](#wip) section out, or ## MSRV -Sabry passes its own tests on 1.78 nightly/stable. +Sabry passes its own tests on 1.88 nightly/stable. -Examples are on 1.82. +Examples are on 1.88. ## License diff --git a/examples/define-styles/Cargo.toml b/examples/define-styles/Cargo.toml index 4288abb..e7a8b65 100644 --- a/examples/define-styles/Cargo.toml +++ b/examples/define-styles/Cargo.toml @@ -2,8 +2,8 @@ name = "define-styles" version = "0.0.0" edition = "2021" -rust-version = "1.82" +rust-version = "1.88" publish = false [dependencies] -sabry = {path = "../../", features = ["nightly"]} +sabry = { path = "../../", features = ["nightly"] } diff --git a/examples/leptos-axum/Cargo.toml b/examples/leptos-axum/Cargo.toml index 659fbf9..162c94a 100644 --- a/examples/leptos-axum/Cargo.toml +++ b/examples/leptos-axum/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "leptos-axum-sabry" version = "0.1.0" -rust-version = "1.82" +rust-version = "1.88" edition = "2021" publish = false @@ -9,31 +9,31 @@ publish = false crate-type = ["cdylib", "rlib"] [dependencies] -leptos = { version = "^0.7" } -leptos_axum = { version = "^0.7", optional = true } -leptos_meta = { version = "^0.7" } -leptos_router = { version = "^0.7" } -axum = { version = "0.7", optional = true } +leptos = { version = "^0.8" } +leptos_axum = { version = "^0.8", optional = true } +leptos_meta = { version = "^0.8" } +leptos_router = { version = "^0.8" } +axum = { version = "0.8", optional = true } console_error_panic_hook = "0.1" tokio = { version = "1", features = ["rt-multi-thread"], optional = true } -tower = { version = "0.4", optional = true } -tower-http = { version = "0.5", features = ["fs"], optional = true } +tower = { version = "0.5", optional = true } +tower-http = { version = "0.6", features = ["fs"], optional = true } wasm-bindgen = "=0.2.96" -thiserror = "1" +thiserror = "^1" tracing = { version = "0.1", optional = true } -http = "1" +http = "^1" # sabry with sass -sabry = {path = "../../", features = ["lepty-scoping"]} +sabry = { path = "../../" } # our component library -leptos-components = {path = "../leptos-components"} +leptos-components = { path = "../leptos-components" } [build-dependencies] # sabry for build with sass -sabry = {path = "../../", features = ["build", "lepty-scoping"]} +sabry = { path = "../../", features = ["build"] } # lets depend on our styles -define-styles = {path = "../define-styles"} +define-styles = { path = "../define-styles" } [features] hydrate = ["leptos/hydrate"] diff --git a/examples/leptos-components/Cargo.toml b/examples/leptos-components/Cargo.toml index 9e8b6d6..2d0f28b 100644 --- a/examples/leptos-components/Cargo.toml +++ b/examples/leptos-components/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "leptos-components" version = "0.1.0" -rust-version = "1.82" +rust-version = "1.88" edition = "2021" publish = false @@ -13,14 +13,10 @@ leptos = { version = "^0.7" } leptos_meta = { version = "^0.7" } leptos_router = { version = "^0.7" } -sabry = { path = "../../", features = ["lepty-scoping", "const-scoping"] } +sabry = { path = "../../", features = ["const-scoping"] } [build-dependencies] -sabry = { path = "../../", features = [ - "build", - "lepty-scoping", - "const-scoping", -] } +sabry = { path = "../../", features = ["build", "const-scoping"] } define-styles = { path = "../define-styles" } [features] diff --git a/examples/use-styles/Cargo.toml b/examples/use-styles/Cargo.toml index c6241e0..fa3bd23 100644 --- a/examples/use-styles/Cargo.toml +++ b/examples/use-styles/Cargo.toml @@ -2,15 +2,15 @@ name = "use-styles" version = "0.0.0" edition = "2021" -rust-version = "1.82" +rust-version = "1.88" publish = false [dependencies] -sabry = {path = "../../", features = ["nightly"]} +sabry = { path = "../../", features = ["nightly"] } [build-dependencies] -sabry = {path = "../../", features = ["build", "nightly"]} -define-styles = {path = "../define-styles"} +sabry = { path = "../../", features = ["build", "nightly"] } +define-styles = { path = "../define-styles" } [package.metadata.sabry] css.bundle = "target/static/style.css" @@ -21,4 +21,4 @@ hash.size = 6 hash.collision = "error" [package.metadata.sabry.lightningcss] -targets = {chrome = "120", firefox = "120", ie = "8"} +targets = { chrome = "120", firefox = "120", ie = "8" } diff --git a/sabry_intrnl/Cargo.toml b/sabry_intrnl/Cargo.toml index b39c615..939f3e1 100644 --- a/sabry_intrnl/Cargo.toml +++ b/sabry_intrnl/Cargo.toml @@ -16,7 +16,6 @@ regex.optional = true grass.workspace = true grass.optional = true lightningcss.workspace = true -lightningcss.features = ["grid"] lightningcss.optional = true blake3.workspace = true blake3.optional = true @@ -30,7 +29,6 @@ toml.workspace = true cfg-if.workspace = true [features] -lepty-scoping = [] scope = ["dep:blake3", "dep:base64", "dep:regex"] compile = ["dep:lightningcss", "dep:grass"] diff --git a/sabry_intrnl/src/config/mod.rs b/sabry_intrnl/src/config/mod.rs index dfa13bc..5c4f63b 100644 --- a/sabry_intrnl/src/config/mod.rs +++ b/sabry_intrnl/src/config/mod.rs @@ -217,31 +217,21 @@ impl From for lightningcss::targets::Browsers { } /// \[package.metadata.sabry.sass\].module_name_collision option -#[derive(Deserialize, Clone, Copy)] +#[derive(Default, Deserialize, Clone, Copy)] #[serde(rename_all = "lowercase")] pub enum BehavSassModCollision { + #[default] Merge, Error, } -impl Default for BehavSassModCollision { - fn default() -> Self { - Self::Merge - } -} - /// \[package.metadata.sabry.hash\].collision option -#[derive(Deserialize, Clone, Copy)] +#[derive(Default, Deserialize, Clone, Copy)] #[serde(rename_all = "lowercase")] pub enum BehavHashCollision { - Error, + #[default] Ignore, -} - -impl Default for BehavHashCollision { - fn default() -> Self { - Self::Ignore - } + Error, } #[cfg(test)] diff --git a/sabry_intrnl/src/scoper/hash.rs b/sabry_intrnl/src/scoper/hash.rs index df21766..34ef46d 100644 --- a/sabry_intrnl/src/scoper/hash.rs +++ b/sabry_intrnl/src/scoper/hash.rs @@ -1,3 +1,5 @@ +use raffia::ast::InterpolableIdent; + /// Convenience wrapper for String-being-a-hash #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ScopeHash(String); @@ -30,7 +32,10 @@ impl ScopeHash { .iter() .map(|s| &s.name) .chain(ids.iter().map(|i| &i.name)) - .map(|ident| ident.as_literal()) + .map(|ident| match ident { + InterpolableIdent::Literal(lit) => Some(lit), + _ => None, + }) .filter_map(|mbl| mbl.map(|l| l.raw)) .collect::(); diff --git a/sabry_intrnl/src/scoper/mod.rs b/sabry_intrnl/src/scoper/mod.rs index 408b417..2b0d2dd 100644 --- a/sabry_intrnl/src/scoper/mod.rs +++ b/sabry_intrnl/src/scoper/mod.rs @@ -1,8 +1,10 @@ use std::fmt::Debug; -use cfg_if::cfg_if; use hash::ScopeHash; -use raffia::{Span, Spanned}; +use raffia::{ + ast::{InterpolableIdent, TypeSelector}, + Span, Spanned, +}; use crate::syntax::{ostrta::OneSyntaxToRuleThemAll, StylesheetAdapter}; @@ -106,7 +108,10 @@ impl<'s> HashedScope<'s> { let tags = scope.adapter().type_selectors(); let tags = tags .iter() - .filter_map(|t| t.as_tag_name()) + .filter_map(|t| match t { + TypeSelector::TagName(tag) => Some(tag), + _ => None, + }) .map(|c| HashedSelector::from_tag(&hash, c)); // get global selectors @@ -234,29 +239,17 @@ impl HashedSelector { /// whats this function returns will be usable in HTML with to_hashed_html_def() pub fn make_hashed_css(value: &ScopedSelector, hash: &ScopeHash) -> String { match value { - // Class scoping is done with class composition ScopedSelector::Class(a) => { + // Class scoping is done by composition format!("{}.{}", hash.as_str(), a.ident) } - // As we dont want to use two HTML props for a single ID, - // ID scoping is done with id modification - ScopedSelector::Id(a) => { - cfg_if! { - if #[cfg(feature = "lepty-scoping")] { - format!("{}.{}", a.ident, hash.as_str()) - } else { - format!("{}-{}", hash.as_str(), a.ident) - } - } - } ScopedSelector::Tag(a) => { - cfg_if! { - if #[cfg(feature = "lepty-scoping")] { - format!("{}.{}", a.ident, hash.as_str()) - } else { - format!(".{} {}", hash.as_str(), a.ident) - } - } + // Tag scoping is done by composition + format!("{}.{}", a.ident, hash.as_str()) + } + ScopedSelector::Id(a) => { + // ID scoping is done by class composition + format!("{}.{}", a.ident, hash.as_str()) } ScopedSelector::Glob { raw, .. } => raw.clone(), } @@ -267,31 +260,15 @@ impl HashedSelector { /// Cooperates with the [HashedSelector::make_hashed_css], so /// whats this function returns will be usable in CSS with to_hashed_code() /// - /// Not every hashed selector is presentable for HTML-ish use: like `div`, in that case - /// returns [None] - pub fn make_hashed_html(value: &ScopedSelector, _hash: &ScopeHash) -> Option { + /// Since 0.0.6 all scoping is done via classes, so this function + /// persists for bw-compat for everything except for class. + /// + /// Global and Tag selectors are never transformed; + pub fn make_hashed_html(value: &ScopedSelector, hash: &ScopeHash) -> Option { match value { // Class scoping is done with class composition - ScopedSelector::Class(a) => { - cfg_if! { - if #[cfg(feature = "lepty-scoping")] { - Some(a.ident.to_string()) - } else { - Some(format!("{} {}", _hash.as_str(), a.ident)) - } - } - } - // As we dont want to use two HTML props for a single ID, - // ID scoping is done with id modification - ScopedSelector::Id(a) => { - cfg_if! { - if #[cfg(feature = "lepty-scoping")] { - Some(a.ident.to_string()) - } else { - Some(format!("{}-{}", _hash.as_str(), a.ident)) - } - } - } + ScopedSelector::Class(a) => Some(format!("{} {}", a.ident, hash.as_str())), + ScopedSelector::Id(id) => Some(id.ident.clone()), ScopedSelector::Tag(_) => None, ScopedSelector::Glob { .. } => None, } @@ -366,10 +343,9 @@ impl ScopedSelector { /// Construct this from class selector pub fn from_class(s: &raffia::ast::ClassSelector) -> Self { - let lit = s - .name - .as_literal() - .expect("BUG: class selector is not a literal"); + let InterpolableIdent::Literal(lit) = &s.name else { + panic!("BUG: class selector was not a literal") + }; Self::Class(ArbitrarySelector { ident: lit.raw.to_string(), span: lit.span().clone(), @@ -378,10 +354,9 @@ impl ScopedSelector { /// Construct this from id selector pub fn from_id(s: &raffia::ast::IdSelector) -> Self { - let lit = s - .name - .as_literal() - .expect("BUG: id selector is not a literal"); + let InterpolableIdent::Literal(lit) = &s.name else { + panic!("BUG: id selector was not a literal") + }; Self::Id(ArbitrarySelector { ident: lit.raw.to_string(), span: lit.span().clone(), @@ -390,11 +365,9 @@ impl ScopedSelector { /// Construct this from tagname selector pub fn from_tag(s: &raffia::ast::TagNameSelector) -> Self { - let lit = s - .name - .name - .as_literal() - .expect("BUG: tag selector is not a literal"); + let InterpolableIdent::Literal(lit) = &s.name.name else { + panic!("BUG: tag selector was not a literal") + }; Self::Tag(ArbitrarySelector { ident: lit.raw.to_string(), span: lit.span().clone(), @@ -439,7 +412,6 @@ pub fn apply_basic_rusty_member_gen_rules(source: &str) -> String { mod test { use std::collections::HashSet; - use cfg_if::cfg_if; use syn::Ident; use crate::{ @@ -456,33 +428,33 @@ mod test { ".cls1{color:red; &-dark{color: black;} #id1 {color:green;} div {color:blue;}} .cls3#id2{color: black;}"; let hash = ScopeHash::test_init("F2kf8nMs".into()); - cfg_if! { - if #[cfg(feature = "lepty-scoping")]{ - let expect_code = ".F2kf8nMs.cls1{color:red; &-dark{color: black;} #id1.F2kf8nMs {color:green;} div.F2kf8nMs {color:blue;}} .F2kf8nMs.cls3#id2.F2kf8nMs{color: black;}"; - } else { - let expect_code = ".F2kf8nMs.cls1{color:red; &-dark{color: black;} #F2kf8nMs-id1 {color:green;} .F2kf8nMs div {color:blue;}} .F2kf8nMs.cls3#F2kf8nMs-id2{color: black;}"; - } - } + //cfg_if! { + //if #[cfg(feature = "lepty-scoping")]{ + let expect_code = ".F2kf8nMs.cls1{color:red; &-dark{color: black;} #id1.F2kf8nMs {color:green;} div.F2kf8nMs {color:blue;}} .F2kf8nMs.cls3#id2.F2kf8nMs{color: black;}"; + //} else { + // let expect_code = ".F2kf8nMs.cls1{color:red; &-dark{color: black;} #F2kf8nMs-id1 {color:green;} .F2kf8nMs div {color:blue;}} .F2kf8nMs.cls3#F2kf8nMs-id2{color: black;}"; + //} + //} - cfg_if! { - if #[cfg(feature = "lepty-scoping")] { - let expect_selector_htmls = HashSet::from([ - "cls1".to_string(), - "id1".to_string(), - "cls3".to_string(), - "id2".to_string(), - "".to_string(), // comes from `div` which has no html hashed code, still want to check presense - ]); - } else { - let expect_selector_htmls = HashSet::from([ - "F2kf8nMs cls1".to_string(), - "F2kf8nMs-id1".to_string(), - "F2kf8nMs cls3".to_string(), - "F2kf8nMs-id2".to_string(), - "".to_string(), // comes from `div` which has no html hashed code, still want to check presense - ]); - } - } + // cfg_if! { + // if #[cfg(feature = "lepty-scoping")] { + let expect_selector_htmls = HashSet::from([ + "cls1".to_string(), + "id1".to_string(), + "cls3".to_string(), + "id2".to_string(), + "".to_string(), // comes from `div` which has no html hashed code, still want to check presense + ]); + // } else { + // let expect_selector_htmls = HashSet::from([ + // "F2kf8nMs cls1".to_string(), + // "F2kf8nMs-id1".to_string(), + // "F2kf8nMs cls3".to_string(), + // "F2kf8nMs-id2".to_string(), + // "".to_string(), // comes from `div` which has no html hashed code, still want to check presense + // ]); + // } + //} let scope = ArbitraryScope::from_source( OneSyntaxToRuleThemAll::Scss, diff --git a/sabry_intrnl/src/syntax/mod.rs b/sabry_intrnl/src/syntax/mod.rs index 9feccd4..d1d8c67 100644 --- a/sabry_intrnl/src/syntax/mod.rs +++ b/sabry_intrnl/src/syntax/mod.rs @@ -1,8 +1,8 @@ use ostrta::OneSyntaxToRuleThemAll; use raffia::{ ast::{ - ClassSelector, CompoundSelector, IdSelector, NestingSelector, PseudoClassSelector, - SimpleSelector, Statement, Stylesheet, TypeSelector, + ClassSelector, ComplexSelectorChild, CompoundSelector, IdSelector, InterpolableIdent, + NestingSelector, PseudoClassSelector, SimpleSelector, Statement, Stylesheet, TypeSelector, }, ParserBuilder, }; @@ -41,38 +41,49 @@ impl<'s> StylesheetAdapter<'s> { } pub fn class_selectors(&self) -> Vec> { - self.selectors_by(|c| c.as_class().cloned()) + self.selectors_by(|c| match c { + SimpleSelector::Class(sel) => Some(sel), + _ => None, + }) } pub fn id_selectors(&self) -> Vec> { - self.selectors_by(|c| c.as_id().cloned()) + self.selectors_by(|c| match c { + SimpleSelector::Id(sel) => Some(sel), + _ => None, + }) } pub fn type_selectors(&self) -> Vec> { - self.selectors_by(|c| c.as_type().cloned()) + self.selectors_by(|c| match c { + SimpleSelector::Type(sel) => Some(sel), + _ => None, + }) } pub fn nesting_selectors(&self) -> Vec> { - self.selectors_by(|c| c.as_nesting().cloned()) + self.selectors_by(|c| match c { + SimpleSelector::Nesting(sel) => Some(sel), + _ => None, + }) } /// special selector case searching for :g(whatever), :glob(whatever), :global(whatever) pub fn glob_modified_selectors(&self) -> Vec> { self.selectors_by(|c| { - let cps = match c.as_pseudo_class() { - Some(s) => s.clone(), + let cps = match c { + SimpleSelector::PseudoClass(sel) => sel, _ => return None, }; - let ident = match cps.name.as_literal().map(|r| r.raw) { - Some(i) => i, - _ => return None, - }; - - if ident == "global" { - Some(cps) - } else { - None + match &cps.name { + InterpolableIdent::Literal(lit) => match lit.raw { + "g" => Some(cps), + "glob" => Some(cps), + "global" => Some(cps), + _ => None, + }, + _ => None, } }) } @@ -106,21 +117,27 @@ impl<'s> StylesheetAdapter<'s> { // 4 - Danik // 5 - Yiffy // 8 - Danik - c.filter_map(|s| s.as_qualified_rule().cloned()) - .flat_map(|q| { - let thissels = q - .selector - .selectors - .iter() - .flat_map(|sel| &sel.children) - .filter_map(|sc| sc.as_compound_selector()) - .cloned(); + c.filter_map(|s| match s { + Statement::QualifiedRule(stmt) => Some(stmt), + _ => None, + }) + .flat_map(|q| { + let thissels = q + .selector + .selectors + .iter() + .flat_map(|sel| &sel.children) + .filter_map(|sc| match sc { + ComplexSelectorChild::CompoundSelector(sel) => Some(sel), + _ => None, + }) + .cloned(); - let blockstmt = q.block.statements.iter().cloned(); - Self::selectors_of(blockstmt) - .chain(thissels) - .collect::>() - }) + let blockstmt = q.block.statements.iter().cloned(); + Self::selectors_of(blockstmt) + .chain(thissels) + .collect::>() + }) } } @@ -128,7 +145,10 @@ impl<'s> StylesheetAdapter<'s> { mod test { use std::collections::HashSet; - use raffia::Spanned; + use raffia::{ + ast::{InterpolableIdent, TypeSelector}, + Spanned, + }; use crate::syntax::ostrta::OneSyntaxToRuleThemAll; @@ -191,20 +211,32 @@ div#id2 let classes = adp .class_selectors() .iter() - .filter_map(|c| c.name.as_literal().map(|l| l.raw)) + .filter_map(|c| match &c.name { + InterpolableIdent::Literal(lit) => Some(lit.raw), + _ => None, + }) .collect::>(); let ids = adp .id_selectors() .iter() - .filter_map(|i| i.name.as_literal().map(|l| l.raw)) + .filter_map(|i| match &i.name { + InterpolableIdent::Literal(lit) => Some(lit.raw), + _ => None, + }) .collect::>(); let tags = adp .type_selectors() .iter() - .filter_map(|t| t.as_tag_name()) - .filter_map(|t| t.name.name.as_literal().map(|l| l.raw)) + .filter_map(|t| match t { + TypeSelector::TagName(tag) => Some(tag), + _ => None, + }) + .filter_map(|t| match &t.name.name { + InterpolableIdent::Literal(lit) => Some(lit.raw), + _ => None, + }) .collect::>(); assert_eq!(expect_classes, classes); @@ -217,20 +249,32 @@ div#id2 let classes = adp .class_selectors() .iter() - .filter_map(|c| c.name.as_literal().map(|l| l.raw)) + .filter_map(|c| match &c.name { + InterpolableIdent::Literal(lit) => Some(lit.raw), + _ => None, + }) .collect::>(); let ids = adp .id_selectors() .iter() - .filter_map(|i| i.name.as_literal().map(|l| l.raw)) + .filter_map(|i| match &i.name { + InterpolableIdent::Literal(lit) => Some(lit.raw), + _ => None, + }) .collect::>(); let tags = adp .type_selectors() .iter() - .filter_map(|t| t.as_tag_name()) - .filter_map(|t| t.name.name.as_literal().map(|l| l.raw)) + .filter_map(|t| match t { + TypeSelector::TagName(tag) => Some(tag), + _ => None, + }) + .filter_map(|t| match &t.name.name { + InterpolableIdent::Literal(lit) => Some(lit.raw), + _ => None, + }) .collect::>(); assert_eq!(expect_classes, classes); diff --git a/sabry_intrnl/src/syntax/ostrta.rs b/sabry_intrnl/src/syntax/ostrta.rs index 8d6826c..bf320a0 100644 --- a/sabry_intrnl/src/syntax/ostrta.rs +++ b/sabry_intrnl/src/syntax/ostrta.rs @@ -1,15 +1,10 @@ /// Convenience unification entrypoint /// to make friends with all the different syntax-consumers -#[derive(Debug, Clone, Copy)] +#[derive(Default, Debug, Clone, Copy)] pub enum OneSyntaxToRuleThemAll { - Sass, + #[default] Scss, -} - -impl Default for OneSyntaxToRuleThemAll { - fn default() -> Self { - Self::Scss - } + Sass, } impl TryFrom<&str> for OneSyntaxToRuleThemAll { diff --git a/sabry_procmacro_impl/Cargo.toml b/sabry_procmacro_impl/Cargo.toml index ae8189a..05646ba 100644 --- a/sabry_procmacro_impl/Cargo.toml +++ b/sabry_procmacro_impl/Cargo.toml @@ -11,6 +11,7 @@ license.workspace = true [dependencies] sabry_intrnl.workspace = true sabry_intrnl.features = ["scope"] +raffia.workspace = true syn.workspace = true syn.features = ["parsing"] quote.workspace = true diff --git a/sabry_procmacro_impl/src/impls/styly.rs b/sabry_procmacro_impl/src/impls/styly.rs index a0a7e23..0297c06 100644 --- a/sabry_procmacro_impl/src/impls/styly.rs +++ b/sabry_procmacro_impl/src/impls/styly.rs @@ -84,10 +84,17 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok .iter() .filter_map(|ns| { ns.suffix.clone().and_then(|s| { - let interp = s - .as_sass_interpolated() - .and_then(|i| i.elements.first()) - .and_then(|e| e.as_static().map(|s| s.raw.to_string())); + let interp = match s { + raffia::ast::InterpolableIdent::SassInterpolated(int) => Some(int), + _ => None, + }; + + let interp = interp.and_then(|i| match i.elements.first() { + Some(raffia::ast::SassInterpolatedIdentElement::Static(i)) => { + Some(i.raw.to_string()) + } + _ => None, + }); interp }) @@ -102,6 +109,10 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok quote! { #[doc = #doc] + #[deprecated( + since = "0.0.6", + note = "All scoping is done by directly applying hash classes to elements now - this is useless for everything except of classes" + )] pub fn #fnident(c: &str) -> String {format!(#formatstr)} } }); @@ -120,23 +131,35 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option) -> Tok ScopedSelector::Glob { .. } => "glob", }; - (name, hs.sel.gen_rusty_ident(), html, hs.css_ident.clone()) + ( + name, + hs.sel.gen_rusty_ident(), + html, + hs.css_ident.clone(), + ) }) }) .map(|(name, ident, html, css)| { let docs = format!("'{ident:?}' {name}. CSS selector '{css}'"); quote! { #[doc = #docs] + #[deprecated( + since = "0.0.6", + note = "All scoping is done by directly applying hash classes to elements now - this is useless for everything except of classes" + )] #[allow(non_upper_case_globals)] pub const #ident : &str = #html ; } }); let mod_docs = format!( - "'{}' style scope. The wrapper class for scoped tagnames is {}", + "'{}' style scope. The hash class is `{}`", &scope_ident, &scope_wrapper_ident ); - 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); + let wrp_docs = format!( + "hash class for '{}' scope. All HTML elements should have this class applied", + &scope_ident + ); if constant { cfg_if! {