refactor!: remove lepty-scoping feature and make its behaviour the default; deprecate scope members; bump MSRV; bump deps
This commit is contained in:
parent
293de0a1d1
commit
ee1b401f47
6 changed files with 284 additions and 374 deletions
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -217,31 +217,21 @@ impl From<SabryLightTargets> 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)]
|
||||
|
|
|
|||
|
|
@ -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::<String>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
/// 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<String> {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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<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>> {
|
||||
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>> {
|
||||
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>> {
|
||||
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<PseudoClassSelector<'s>> {
|
||||
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::<Vec<_>>()
|
||||
})
|
||||
let blockstmt = q.block.statements.iter().cloned();
|
||||
Self::selectors_of(blockstmt)
|
||||
.chain(thissels)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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::<HashSet<_>>();
|
||||
|
||||
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::<HashSet<_>>();
|
||||
|
||||
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::<HashSet<_>>();
|
||||
|
||||
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::<HashSet<_>>();
|
||||
|
||||
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::<HashSet<_>>();
|
||||
|
||||
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::<HashSet<_>>();
|
||||
|
||||
assert_eq!(expect_classes, classes);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue