refactor!: remove lepty-scoping feature and make its behaviour the default; deprecate scope members; bump MSRV; bump deps
This commit is contained in:
parent
c9ac3484d2
commit
6690ae1751
15 changed files with 284 additions and 374 deletions
2
.github/workflows/cbt.yml
vendored
2
.github/workflows/cbt.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
||||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
- run: cargo build --verbose
|
- run: cargo build --verbose
|
||||||
- run: cargo test --verbose --all
|
- run: cargo test --verbose --all
|
||||||
- run: cargo test --verbose --all --features "const-scoping lepty-scoping"
|
- run: cargo test --verbose --all --features "const-scoping"
|
||||||
|
|
||||||
build_examples:
|
build_examples:
|
||||||
name: Sabry - latset - examples
|
name: Sabry - latset - examples
|
||||||
|
|
|
||||||
16
Cargo.toml
16
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 = { path = "sabry_procmacro", version = "0.0.6-rc.1" }
|
||||||
sabry_procmacro_impl = { path = "sabry_procmacro_impl", 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" }
|
sabry_build = { path = "sabry_build", version = "0.0.6-rc.1" }
|
||||||
## Package
|
#/
|
||||||
## Package
|
lightningcss.version = "1.0.0-alpha.67"
|
||||||
lightningcss.version = "1.0.0-alpha.65"
|
|
||||||
lightningcss.default-features = false
|
lightningcss.default-features = false
|
||||||
grass.version = "^0.13"
|
grass.version = "^0.13"
|
||||||
grass.default-features = false
|
grass.default-features = false
|
||||||
raffia.version = "^0.9"
|
raffia.version = "^0.11"
|
||||||
#/
|
#/
|
||||||
serde.version = "^1"
|
serde.version = "^1"
|
||||||
toml.version = "^0.8"
|
toml.version = "^0.9"
|
||||||
blake3.version = "^1"
|
blake3.version = "^1"
|
||||||
base64.version = "^0.22"
|
base64.version = "^0.22"
|
||||||
regex.version = "^1"
|
regex.version = "^1"
|
||||||
|
|
@ -35,7 +34,7 @@ syn.version = "^2"
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.6-rc.1"
|
version = "0.0.6-rc.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.78"
|
rust-version = "1.88"
|
||||||
authors = ["Svyatoslav Pushkarev <yiffyrusdev@yandex.ru>"]
|
authors = ["Svyatoslav Pushkarev <yiffyrusdev@yandex.ru>"]
|
||||||
repository = "https://github.com/yiffyrusdev/sabry"
|
repository = "https://github.com/yiffyrusdev/sabry"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
@ -60,12 +59,11 @@ sabry_build = { workspace = true, optional = true }
|
||||||
sabry_intrnl = { workspace = true }
|
sabry_intrnl = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
leptos = { version = "0.7.0-gamma3" }
|
leptos = { version = "^0.8" }
|
||||||
insta = { version = "^1.41", features = ["glob"] }
|
insta = { version = "^1", features = ["glob"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["procmacro"]
|
default = ["procmacro"]
|
||||||
lepty-scoping = ["sabry_intrnl/lepty-scoping"]
|
|
||||||
const-scoping = ["sabry_procmacro?/const-compile"]
|
const-scoping = ["sabry_procmacro?/const-compile"]
|
||||||
procmacro = ["dep:sabry_procmacro"]
|
procmacro = ["dep:sabry_procmacro"]
|
||||||
build = ["dep:sabry_build"]
|
build = ["dep:sabry_build"]
|
||||||
|
|
|
||||||
178
README.md
178
README.md
|
|
@ -25,17 +25,6 @@ sabry::scssy!(tokens {"@mixin colored{color: red;}"});
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
Get the *(almost)* proper autocompletion for your scope from rust-analyzer
|
|
||||||
|
|
||||||
```rust
|
|
||||||
sabry::styly!(scope {".cls1{} #id1{}"});
|
|
||||||
/* autocompleted for you */
|
|
||||||
scope::cls1;
|
|
||||||
scope::theid1;
|
|
||||||
```
|
|
||||||
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
Code your SASS in separate files to get proper syntax highlighting, or do sass-in-the-rust to keep things in one place
|
Code your SASS in separate files to get proper syntax highlighting, or do sass-in-the-rust to keep things in one place
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
@ -71,13 +60,23 @@ Keep things as private and modular as you wish
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
sabry::styly!(pub cats "tests/assets/mixin-module.scss");
|
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
|
/// something like render function
|
||||||
{
|
{
|
||||||
<div class={cats::meow}>
|
<div class={concatcp!("meow ", CATS)}>
|
||||||
<ul id={dogs::thehowl}></ul>
|
<ul class={concatcp!("howl ", DOGS)}></ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```html
|
||||||
|
/// or even better with leptos
|
||||||
|
view!{class=CATS
|
||||||
|
<div class="meow">
|
||||||
|
{move || view!{class=DOGS,
|
||||||
|
<ul class="howl"></ul>
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -122,7 +121,7 @@ The only need is the dependency
|
||||||
#Cargo.toml
|
#Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.5"}
|
sabry = {version = "0.0.6"}
|
||||||
```
|
```
|
||||||
And a proc-macro
|
And a proc-macro
|
||||||
```rust
|
```rust
|
||||||
|
|
@ -155,7 +154,7 @@ Depend on sabry
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.5"}
|
sabry = {version = "0.0.6"}
|
||||||
```
|
```
|
||||||
And create a style scope wherever you want:
|
And create a style scope wherever you want:
|
||||||
```rust
|
```rust
|
||||||
|
|
@ -173,18 +172,18 @@ styly!(styles {"
|
||||||
color: white;
|
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
|
||||||
|
<ul class={concatcp!("badges ", STYLES)}>
|
||||||
|
<li class={STYLES} id="wolf">
|
||||||
|
"HOOOOWL!"
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
### Use styles earlier created in another crate
|
### 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.
|
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.
|
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
|
# Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.5"}
|
sabry = {version = "0.0.6"}
|
||||||
|
|
||||||
[build-dependencies]
|
[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.
|
> 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
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### 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 */
|
|
||||||
<h1>"Head"</h1>
|
|
||||||
<p>"text"</p>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
... 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,
|
|
||||||
<h1>
|
|
||||||
"Head"
|
|
||||||
<img src="whatever"/>
|
|
||||||
</h1>
|
|
||||||
<ul class="breadcumbs">
|
|
||||||
<li class="breadcumbs__item">
|
|
||||||
"Home page"
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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: `<li class="breadcumbs__item">`
|
|
||||||
- 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
|
## Configuration
|
||||||
|
|
||||||
Sabry configuration lives in `[package.metadata.sabry]` table of the manifest file.
|
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
|
```rust,ignore
|
||||||
const FOX: &str = "J9k_s9";
|
const FOX: &str = "J9k_s9";
|
||||||
mod fox {
|
mod fox {
|
||||||
pub const fur: &str = "J9k_s9 fur";
|
#[deprecated(since = "0.0.6", note = "will remove")]
|
||||||
pub fn _dark(c: &str) -> String {format!("{c}-dark")}
|
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:
|
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
|
- `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.
|
You can read more about scoping and hashing in the [scoping](#scoping) section.
|
||||||
|
|
||||||
|
|
@ -600,7 +521,6 @@ Which results in following:
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
const SCOPE: &str = /* scope hash */;
|
const SCOPE: &str = /* scope hash */;
|
||||||
const SCOPE_CSS: &str = /* compiled from src/assets/scope.scss */;
|
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:
|
> **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
|
### 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.
|
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:
|
Currently the following types of selectors are scoped:
|
||||||
|
|
||||||
- class
|
- class
|
||||||
- id
|
- id
|
||||||
|
- tagname
|
||||||
- SASS parent selectors (see below, there is a catch)
|
- 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,
|
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
|
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:
|
Different selector types are scoped differently:
|
||||||
|
|
||||||
- class selectors are restricted with scope hash: `.class` -> `.HASH.class`
|
- class selectors are restricted with scope hash: `.class` -> `.HASH.class`
|
||||||
- id selector are mutated with scope hash: `#id` -> `#id-HASH`
|
- id selector are mutated with scope hash: `#id` -> `.HASH#id`
|
||||||
- tagname selectors are changed into indirect descendance: `div` -> `.HASH div`
|
- tagname selectors are changed into indirect descendance: `div` -> `div.HASH`
|
||||||
|
|
||||||
> **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.
|
|
||||||
|
|
||||||
#### Scope member naming rules
|
#### 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
|
## 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
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name = "define-styles"
|
name = "define-styles"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.82"
|
rust-version = "1.88"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "leptos-axum-sabry"
|
name = "leptos-axum-sabry"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.82"
|
rust-version = "1.88"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
@ -9,29 +9,29 @@ publish = false
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
leptos = { version = "^0.7" }
|
leptos = { version = "^0.8" }
|
||||||
leptos_axum = { version = "^0.7", optional = true }
|
leptos_axum = { version = "^0.8", optional = true }
|
||||||
leptos_meta = { version = "^0.7" }
|
leptos_meta = { version = "^0.8" }
|
||||||
leptos_router = { version = "^0.7" }
|
leptos_router = { version = "^0.8" }
|
||||||
axum = { version = "0.7", optional = true }
|
axum = { version = "0.8", optional = true }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
|
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
|
||||||
tower = { version = "0.4", optional = true }
|
tower = { version = "0.5", optional = true }
|
||||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
tower-http = { version = "0.6", features = ["fs"], optional = true }
|
||||||
wasm-bindgen = "=0.2.96"
|
wasm-bindgen = "=0.2.96"
|
||||||
thiserror = "1"
|
thiserror = "^1"
|
||||||
tracing = { version = "0.1", optional = true }
|
tracing = { version = "0.1", optional = true }
|
||||||
http = "1"
|
http = "^1"
|
||||||
|
|
||||||
# sabry with sass
|
# sabry with sass
|
||||||
sabry = {path = "../../", features = ["lepty-scoping"]}
|
sabry = { path = "../../" }
|
||||||
|
|
||||||
# our component library
|
# our component library
|
||||||
leptos-components = { path = "../leptos-components" }
|
leptos-components = { path = "../leptos-components" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
# sabry for build with sass
|
# sabry for build with sass
|
||||||
sabry = {path = "../../", features = ["build", "lepty-scoping"]}
|
sabry = { path = "../../", features = ["build"] }
|
||||||
# lets depend on our styles
|
# lets depend on our styles
|
||||||
define-styles = { path = "../define-styles" }
|
define-styles = { path = "../define-styles" }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "leptos-components"
|
name = "leptos-components"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.82"
|
rust-version = "1.88"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
@ -13,14 +13,10 @@ leptos = { version = "^0.7" }
|
||||||
leptos_meta = { version = "^0.7" }
|
leptos_meta = { version = "^0.7" }
|
||||||
leptos_router = { version = "^0.7" }
|
leptos_router = { version = "^0.7" }
|
||||||
|
|
||||||
sabry = { path = "../../", features = ["lepty-scoping", "const-scoping"] }
|
sabry = { path = "../../", features = ["const-scoping"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
sabry = { path = "../../", features = [
|
sabry = { path = "../../", features = ["build", "const-scoping"] }
|
||||||
"build",
|
|
||||||
"lepty-scoping",
|
|
||||||
"const-scoping",
|
|
||||||
] }
|
|
||||||
define-styles = { path = "../define-styles" }
|
define-styles = { path = "../define-styles" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name = "use-styles"
|
name = "use-styles"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.82"
|
rust-version = "1.88"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ regex.optional = true
|
||||||
grass.workspace = true
|
grass.workspace = true
|
||||||
grass.optional = true
|
grass.optional = true
|
||||||
lightningcss.workspace = true
|
lightningcss.workspace = true
|
||||||
lightningcss.features = ["grid"]
|
|
||||||
lightningcss.optional = true
|
lightningcss.optional = true
|
||||||
blake3.workspace = true
|
blake3.workspace = true
|
||||||
blake3.optional = true
|
blake3.optional = true
|
||||||
|
|
@ -30,7 +29,6 @@ toml.workspace = true
|
||||||
cfg-if.workspace = true
|
cfg-if.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
lepty-scoping = []
|
|
||||||
scope = ["dep:blake3", "dep:base64", "dep:regex"]
|
scope = ["dep:blake3", "dep:base64", "dep:regex"]
|
||||||
compile = ["dep:lightningcss", "dep:grass"]
|
compile = ["dep:lightningcss", "dep:grass"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -217,31 +217,21 @@ impl From<SabryLightTargets> for lightningcss::targets::Browsers {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \[package.metadata.sabry.sass\].module_name_collision option
|
/// \[package.metadata.sabry.sass\].module_name_collision option
|
||||||
#[derive(Deserialize, Clone, Copy)]
|
#[derive(Default, Deserialize, Clone, Copy)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum BehavSassModCollision {
|
pub enum BehavSassModCollision {
|
||||||
|
#[default]
|
||||||
Merge,
|
Merge,
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BehavSassModCollision {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Merge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \[package.metadata.sabry.hash\].collision option
|
/// \[package.metadata.sabry.hash\].collision option
|
||||||
#[derive(Deserialize, Clone, Copy)]
|
#[derive(Default, Deserialize, Clone, Copy)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum BehavHashCollision {
|
pub enum BehavHashCollision {
|
||||||
Error,
|
#[default]
|
||||||
Ignore,
|
Ignore,
|
||||||
}
|
Error,
|
||||||
|
|
||||||
impl Default for BehavHashCollision {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use raffia::ast::InterpolableIdent;
|
||||||
|
|
||||||
/// Convenience wrapper for String-being-a-hash
|
/// Convenience wrapper for String-being-a-hash
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ScopeHash(String);
|
pub struct ScopeHash(String);
|
||||||
|
|
@ -30,7 +32,10 @@ impl ScopeHash {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| &s.name)
|
.map(|s| &s.name)
|
||||||
.chain(ids.iter().map(|i| &i.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))
|
.filter_map(|mbl| mbl.map(|l| l.raw))
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
use hash::ScopeHash;
|
use hash::ScopeHash;
|
||||||
use raffia::{Span, Spanned};
|
use raffia::{
|
||||||
|
ast::{InterpolableIdent, TypeSelector},
|
||||||
|
Span, Spanned,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::syntax::{ostrta::OneSyntaxToRuleThemAll, StylesheetAdapter};
|
use crate::syntax::{ostrta::OneSyntaxToRuleThemAll, StylesheetAdapter};
|
||||||
|
|
||||||
|
|
@ -106,7 +108,10 @@ impl<'s> HashedScope<'s> {
|
||||||
let tags = scope.adapter().type_selectors();
|
let tags = scope.adapter().type_selectors();
|
||||||
let tags = tags
|
let tags = tags
|
||||||
.iter()
|
.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));
|
.map(|c| HashedSelector::from_tag(&hash, c));
|
||||||
|
|
||||||
// get global selectors
|
// get global selectors
|
||||||
|
|
@ -234,29 +239,17 @@ impl HashedSelector {
|
||||||
/// whats this function returns will be usable in HTML with to_hashed_html_def()
|
/// whats this function returns will be usable in HTML with to_hashed_html_def()
|
||||||
pub fn make_hashed_css(value: &ScopedSelector, hash: &ScopeHash) -> String {
|
pub fn make_hashed_css(value: &ScopedSelector, hash: &ScopeHash) -> String {
|
||||||
match value {
|
match value {
|
||||||
// Class scoping is done with class composition
|
|
||||||
ScopedSelector::Class(a) => {
|
ScopedSelector::Class(a) => {
|
||||||
|
// Class scoping is done by composition
|
||||||
format!("{}.{}", hash.as_str(), a.ident)
|
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) => {
|
ScopedSelector::Tag(a) => {
|
||||||
cfg_if! {
|
// Tag scoping is done by composition
|
||||||
if #[cfg(feature = "lepty-scoping")] {
|
|
||||||
format!("{}.{}", a.ident, hash.as_str())
|
format!("{}.{}", a.ident, hash.as_str())
|
||||||
} else {
|
|
||||||
format!(".{} {}", hash.as_str(), a.ident)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ScopedSelector::Id(a) => {
|
||||||
|
// ID scoping is done by class composition
|
||||||
|
format!("{}.{}", a.ident, hash.as_str())
|
||||||
}
|
}
|
||||||
ScopedSelector::Glob { raw, .. } => raw.clone(),
|
ScopedSelector::Glob { raw, .. } => raw.clone(),
|
||||||
}
|
}
|
||||||
|
|
@ -267,31 +260,15 @@ impl HashedSelector {
|
||||||
/// Cooperates with the [HashedSelector::make_hashed_css], so
|
/// Cooperates with the [HashedSelector::make_hashed_css], so
|
||||||
/// whats this function returns will be usable in CSS with to_hashed_code()
|
/// 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
|
/// Since 0.0.6 all scoping is done via classes, so this function
|
||||||
/// returns [None]
|
/// persists for bw-compat for everything except for class.
|
||||||
pub fn make_hashed_html(value: &ScopedSelector, _hash: &ScopeHash) -> Option<String> {
|
///
|
||||||
|
/// Global and Tag selectors are never transformed;
|
||||||
|
pub fn make_hashed_html(value: &ScopedSelector, hash: &ScopeHash) -> Option<String> {
|
||||||
match value {
|
match value {
|
||||||
// Class scoping is done with class composition
|
// Class scoping is done with class composition
|
||||||
ScopedSelector::Class(a) => {
|
ScopedSelector::Class(a) => Some(format!("{} {}", a.ident, hash.as_str())),
|
||||||
cfg_if! {
|
ScopedSelector::Id(id) => Some(id.ident.clone()),
|
||||||
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::Tag(_) => None,
|
ScopedSelector::Tag(_) => None,
|
||||||
ScopedSelector::Glob { .. } => None,
|
ScopedSelector::Glob { .. } => None,
|
||||||
}
|
}
|
||||||
|
|
@ -366,10 +343,9 @@ impl ScopedSelector {
|
||||||
|
|
||||||
/// Construct this from class selector
|
/// Construct this from class selector
|
||||||
pub fn from_class(s: &raffia::ast::ClassSelector) -> Self {
|
pub fn from_class(s: &raffia::ast::ClassSelector) -> Self {
|
||||||
let lit = s
|
let InterpolableIdent::Literal(lit) = &s.name else {
|
||||||
.name
|
panic!("BUG: class selector was not a literal")
|
||||||
.as_literal()
|
};
|
||||||
.expect("BUG: class selector is not a literal");
|
|
||||||
Self::Class(ArbitrarySelector {
|
Self::Class(ArbitrarySelector {
|
||||||
ident: lit.raw.to_string(),
|
ident: lit.raw.to_string(),
|
||||||
span: lit.span().clone(),
|
span: lit.span().clone(),
|
||||||
|
|
@ -378,10 +354,9 @@ impl ScopedSelector {
|
||||||
|
|
||||||
/// Construct this from id selector
|
/// Construct this from id selector
|
||||||
pub fn from_id(s: &raffia::ast::IdSelector) -> Self {
|
pub fn from_id(s: &raffia::ast::IdSelector) -> Self {
|
||||||
let lit = s
|
let InterpolableIdent::Literal(lit) = &s.name else {
|
||||||
.name
|
panic!("BUG: id selector was not a literal")
|
||||||
.as_literal()
|
};
|
||||||
.expect("BUG: id selector is not a literal");
|
|
||||||
Self::Id(ArbitrarySelector {
|
Self::Id(ArbitrarySelector {
|
||||||
ident: lit.raw.to_string(),
|
ident: lit.raw.to_string(),
|
||||||
span: lit.span().clone(),
|
span: lit.span().clone(),
|
||||||
|
|
@ -390,11 +365,9 @@ impl ScopedSelector {
|
||||||
|
|
||||||
/// Construct this from tagname selector
|
/// Construct this from tagname selector
|
||||||
pub fn from_tag(s: &raffia::ast::TagNameSelector) -> Self {
|
pub fn from_tag(s: &raffia::ast::TagNameSelector) -> Self {
|
||||||
let lit = s
|
let InterpolableIdent::Literal(lit) = &s.name.name else {
|
||||||
.name
|
panic!("BUG: tag selector was not a literal")
|
||||||
.name
|
};
|
||||||
.as_literal()
|
|
||||||
.expect("BUG: tag selector is not a literal");
|
|
||||||
Self::Tag(ArbitrarySelector {
|
Self::Tag(ArbitrarySelector {
|
||||||
ident: lit.raw.to_string(),
|
ident: lit.raw.to_string(),
|
||||||
span: lit.span().clone(),
|
span: lit.span().clone(),
|
||||||
|
|
@ -439,7 +412,6 @@ pub fn apply_basic_rusty_member_gen_rules(source: &str) -> String {
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -456,16 +428,16 @@ mod test {
|
||||||
".cls1{color:red; &-dark{color: black;} #id1 {color:green;} div {color:blue;}} .cls3#id2{color: black;}";
|
".cls1{color:red; &-dark{color: black;} #id1 {color:green;} div {color:blue;}} .cls3#id2{color: black;}";
|
||||||
let hash = ScopeHash::test_init("F2kf8nMs".into());
|
let hash = ScopeHash::test_init("F2kf8nMs".into());
|
||||||
|
|
||||||
cfg_if! {
|
//cfg_if! {
|
||||||
if #[cfg(feature = "lepty-scoping")]{
|
//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;}";
|
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 {
|
//} 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;}";
|
// 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! {
|
// cfg_if! {
|
||||||
if #[cfg(feature = "lepty-scoping")] {
|
// if #[cfg(feature = "lepty-scoping")] {
|
||||||
let expect_selector_htmls = HashSet::from([
|
let expect_selector_htmls = HashSet::from([
|
||||||
"cls1".to_string(),
|
"cls1".to_string(),
|
||||||
"id1".to_string(),
|
"id1".to_string(),
|
||||||
|
|
@ -473,16 +445,16 @@ mod test {
|
||||||
"id2".to_string(),
|
"id2".to_string(),
|
||||||
"".to_string(), // comes from `div` which has no html hashed code, still want to check presense
|
"".to_string(), // comes from `div` which has no html hashed code, still want to check presense
|
||||||
]);
|
]);
|
||||||
} else {
|
// } else {
|
||||||
let expect_selector_htmls = HashSet::from([
|
// let expect_selector_htmls = HashSet::from([
|
||||||
"F2kf8nMs cls1".to_string(),
|
// "F2kf8nMs cls1".to_string(),
|
||||||
"F2kf8nMs-id1".to_string(),
|
// "F2kf8nMs-id1".to_string(),
|
||||||
"F2kf8nMs cls3".to_string(),
|
// "F2kf8nMs cls3".to_string(),
|
||||||
"F2kf8nMs-id2".to_string(),
|
// "F2kf8nMs-id2".to_string(),
|
||||||
"".to_string(), // comes from `div` which has no html hashed code, still want to check presense
|
// "".to_string(), // comes from `div` which has no html hashed code, still want to check presense
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
let scope = ArbitraryScope::from_source(
|
let scope = ArbitraryScope::from_source(
|
||||||
OneSyntaxToRuleThemAll::Scss,
|
OneSyntaxToRuleThemAll::Scss,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use ostrta::OneSyntaxToRuleThemAll;
|
use ostrta::OneSyntaxToRuleThemAll;
|
||||||
use raffia::{
|
use raffia::{
|
||||||
ast::{
|
ast::{
|
||||||
ClassSelector, CompoundSelector, IdSelector, NestingSelector, PseudoClassSelector,
|
ClassSelector, ComplexSelectorChild, CompoundSelector, IdSelector, InterpolableIdent,
|
||||||
SimpleSelector, Statement, Stylesheet, TypeSelector,
|
NestingSelector, PseudoClassSelector, SimpleSelector, Statement, Stylesheet, TypeSelector,
|
||||||
},
|
},
|
||||||
ParserBuilder,
|
ParserBuilder,
|
||||||
};
|
};
|
||||||
|
|
@ -41,38 +41,49 @@ impl<'s> StylesheetAdapter<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn class_selectors(&self) -> Vec<ClassSelector<'s>> {
|
pub fn class_selectors(&self) -> Vec<ClassSelector<'s>> {
|
||||||
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<IdSelector<'s>> {
|
pub fn id_selectors(&self) -> Vec<IdSelector<'s>> {
|
||||||
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<TypeSelector<'s>> {
|
pub fn type_selectors(&self) -> Vec<TypeSelector<'s>> {
|
||||||
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<NestingSelector<'s>> {
|
pub fn nesting_selectors(&self) -> Vec<NestingSelector<'s>> {
|
||||||
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)
|
/// special selector case searching for :g(whatever), :glob(whatever), :global(whatever)
|
||||||
pub fn glob_modified_selectors(&self) -> Vec<PseudoClassSelector<'s>> {
|
pub fn glob_modified_selectors(&self) -> Vec<PseudoClassSelector<'s>> {
|
||||||
self.selectors_by(|c| {
|
self.selectors_by(|c| {
|
||||||
let cps = match c.as_pseudo_class() {
|
let cps = match c {
|
||||||
Some(s) => s.clone(),
|
SimpleSelector::PseudoClass(sel) => sel,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let ident = match cps.name.as_literal().map(|r| r.raw) {
|
match &cps.name {
|
||||||
Some(i) => i,
|
InterpolableIdent::Literal(lit) => match lit.raw {
|
||||||
_ => return None,
|
"g" => Some(cps),
|
||||||
};
|
"glob" => Some(cps),
|
||||||
|
"global" => Some(cps),
|
||||||
if ident == "global" {
|
_ => None,
|
||||||
Some(cps)
|
},
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -106,14 +117,20 @@ impl<'s> StylesheetAdapter<'s> {
|
||||||
// 4 - Danik
|
// 4 - Danik
|
||||||
// 5 - Yiffy
|
// 5 - Yiffy
|
||||||
// 8 - Danik
|
// 8 - Danik
|
||||||
c.filter_map(|s| s.as_qualified_rule().cloned())
|
c.filter_map(|s| match s {
|
||||||
|
Statement::QualifiedRule(stmt) => Some(stmt),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.flat_map(|q| {
|
.flat_map(|q| {
|
||||||
let thissels = q
|
let thissels = q
|
||||||
.selector
|
.selector
|
||||||
.selectors
|
.selectors
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|sel| &sel.children)
|
.flat_map(|sel| &sel.children)
|
||||||
.filter_map(|sc| sc.as_compound_selector())
|
.filter_map(|sc| match sc {
|
||||||
|
ComplexSelectorChild::CompoundSelector(sel) => Some(sel),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
let blockstmt = q.block.statements.iter().cloned();
|
let blockstmt = q.block.statements.iter().cloned();
|
||||||
|
|
@ -128,7 +145,10 @@ impl<'s> StylesheetAdapter<'s> {
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use raffia::Spanned;
|
use raffia::{
|
||||||
|
ast::{InterpolableIdent, TypeSelector},
|
||||||
|
Spanned,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::syntax::ostrta::OneSyntaxToRuleThemAll;
|
use crate::syntax::ostrta::OneSyntaxToRuleThemAll;
|
||||||
|
|
||||||
|
|
@ -191,20 +211,32 @@ div#id2
|
||||||
let classes = adp
|
let classes = adp
|
||||||
.class_selectors()
|
.class_selectors()
|
||||||
.iter()
|
.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::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let ids = adp
|
let ids = adp
|
||||||
.id_selectors()
|
.id_selectors()
|
||||||
.iter()
|
.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::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let tags = adp
|
let tags = adp
|
||||||
.type_selectors()
|
.type_selectors()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|t| t.as_tag_name())
|
.filter_map(|t| match t {
|
||||||
.filter_map(|t| t.name.name.as_literal().map(|l| l.raw))
|
TypeSelector::TagName(tag) => Some(tag),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter_map(|t| match &t.name.name {
|
||||||
|
InterpolableIdent::Literal(lit) => Some(lit.raw),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
assert_eq!(expect_classes, classes);
|
assert_eq!(expect_classes, classes);
|
||||||
|
|
@ -217,20 +249,32 @@ div#id2
|
||||||
let classes = adp
|
let classes = adp
|
||||||
.class_selectors()
|
.class_selectors()
|
||||||
.iter()
|
.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::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let ids = adp
|
let ids = adp
|
||||||
.id_selectors()
|
.id_selectors()
|
||||||
.iter()
|
.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::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let tags = adp
|
let tags = adp
|
||||||
.type_selectors()
|
.type_selectors()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|t| t.as_tag_name())
|
.filter_map(|t| match t {
|
||||||
.filter_map(|t| t.name.name.as_literal().map(|l| l.raw))
|
TypeSelector::TagName(tag) => Some(tag),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter_map(|t| match &t.name.name {
|
||||||
|
InterpolableIdent::Literal(lit) => Some(lit.raw),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
assert_eq!(expect_classes, classes);
|
assert_eq!(expect_classes, classes);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
/// Convenience unification entrypoint
|
/// Convenience unification entrypoint
|
||||||
/// to make friends with all the different syntax-consumers
|
/// to make friends with all the different syntax-consumers
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
pub enum OneSyntaxToRuleThemAll {
|
pub enum OneSyntaxToRuleThemAll {
|
||||||
Sass,
|
#[default]
|
||||||
Scss,
|
Scss,
|
||||||
}
|
Sass,
|
||||||
|
|
||||||
impl Default for OneSyntaxToRuleThemAll {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Scss
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for OneSyntaxToRuleThemAll {
|
impl TryFrom<&str> for OneSyntaxToRuleThemAll {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ license.workspace = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry_intrnl.workspace = true
|
sabry_intrnl.workspace = true
|
||||||
sabry_intrnl.features = ["scope"]
|
sabry_intrnl.features = ["scope"]
|
||||||
|
raffia.workspace = true
|
||||||
syn.workspace = true
|
syn.workspace = true
|
||||||
syn.features = ["parsing"]
|
syn.features = ["parsing"]
|
||||||
quote.workspace = true
|
quote.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,17 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option<PathBuf>) -> Tok
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|ns| {
|
.filter_map(|ns| {
|
||||||
ns.suffix.clone().and_then(|s| {
|
ns.suffix.clone().and_then(|s| {
|
||||||
let interp = s
|
let interp = match s {
|
||||||
.as_sass_interpolated()
|
raffia::ast::InterpolableIdent::SassInterpolated(int) => Some(int),
|
||||||
.and_then(|i| i.elements.first())
|
_ => None,
|
||||||
.and_then(|e| e.as_static().map(|s| s.raw.to_string()));
|
};
|
||||||
|
|
||||||
|
let interp = interp.and_then(|i| match i.elements.first() {
|
||||||
|
Some(raffia::ast::SassInterpolatedIdentElement::Static(i)) => {
|
||||||
|
Some(i.raw.to_string())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
interp
|
interp
|
||||||
})
|
})
|
||||||
|
|
@ -102,6 +109,10 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option<PathBuf>) -> Tok
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #doc]
|
#[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)}
|
pub fn #fnident(c: &str) -> String {format!(#formatstr)}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -120,23 +131,35 @@ pub fn styly_macro_impl(input: TokenStream, source_path: Option<PathBuf>) -> Tok
|
||||||
ScopedSelector::Glob { .. } => "glob",
|
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)| {
|
.map(|(name, ident, html, css)| {
|
||||||
let docs = format!("'{ident:?}' {name}. CSS selector '{css}'");
|
let docs = format!("'{ident:?}' {name}. CSS selector '{css}'");
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[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)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const #ident : &str = #html ;
|
pub const #ident : &str = #html ;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mod_docs = format!(
|
let mod_docs = format!(
|
||||||
"'{}' style scope. The wrapper class for scoped tagnames is {}",
|
"'{}' style scope. The hash class is `{}`",
|
||||||
&scope_ident, &scope_wrapper_ident
|
&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 {
|
if constant {
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue