Skip to content

Commit

Permalink
feat: implementing the base @scope
Browse files Browse the repository at this point in the history
  • Loading branch information
yisibl committed Feb 12, 2023
1 parent 856d460 commit 5d37f2d
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/lib.rs
Expand Up @@ -21823,6 +21823,29 @@ mod tests {
);
}

#[test]
fn test_at_scope() {
minify_test(
r#"
@scope {
.foo {
display: flex;
}
}"#,
"@scope{.foo{display:flex}}",
);
minify_test(
r#"
@scope {
:scope {
display: flex;
color: lightblue;
}
}"#,
"@scope{:scope{color:#add8e6;display:flex}}",
);
}

#[test]
fn test_custom_media() {
custom_media_test(
Expand Down
13 changes: 13 additions & 0 deletions src/parser.rs
Expand Up @@ -7,7 +7,9 @@ use crate::rules::container::{ContainerName, ContainerRule};
use crate::rules::font_palette_values::FontPaletteValuesRule;
use crate::rules::layer::{LayerBlockRule, LayerStatementRule};
use crate::rules::property::PropertyRule;
use crate::rules::scope::ScopeRule;
use crate::rules::viewport::ViewportRule;

use crate::rules::{
counter_style::CounterStyleRule,
custom_media::CustomMediaRule,
Expand Down Expand Up @@ -137,6 +139,10 @@ impl<'a, 'o, 'b, 'i, T> TopLevelRuleParser<'a, 'o, 'i, T> {
pub enum AtRulePrelude<'i, T> {
/// A @font-face rule prelude.
FontFace,

/// A @scope rule prelude.
Scope,

/// A @font-feature-values rule prelude, with its FamilyName list.
FontFeatureValues, //(Vec<FamilyName>),
/// A @font-palette-values rule prelude, with its name.
Expand Down Expand Up @@ -423,6 +429,9 @@ impl<'a, 'o, 'b, 'i, T: crate::traits::AtRuleParser<'i>> AtRuleParser<'i> for Ne
"font-face" => {
Ok(AtRulePrelude::FontFace)
},
"scope" => {
Ok(AtRulePrelude::Scope)
},
// "font-feature-values" => {
// if !cfg!(feature = "gecko") {
// // Support for this rule is not fully implemented in Servo yet.
Expand Down Expand Up @@ -563,6 +572,10 @@ impl<'a, 'o, 'b, 'i, T: crate::traits::AtRuleParser<'i>> AtRuleParser<'i> for Ne
rules: self.parse_nested_rules(input)?,
loc,
})),
AtRulePrelude::Scope => Ok(CssRule::Scope(ScopeRule {
rules: self.parse_nested_rules(input)?,
loc,
})),
AtRulePrelude::Viewport(vendor_prefix) => {
Ok(CssRule::Viewport(ViewportRule {
vendor_prefix,
Expand Down
10 changes: 10 additions & 0 deletions src/rules/mod.rs
Expand Up @@ -50,6 +50,7 @@ pub mod namespace;
pub mod nesting;
pub mod page;
pub mod property;
pub mod scope;
pub mod style;
pub mod supports;
pub mod unknown;
Expand Down Expand Up @@ -88,6 +89,7 @@ use media::MediaRule;
use namespace::NamespaceRule;
use nesting::NestingRule;
use page::PageRule;
use scope::ScopeRule;
use std::collections::{HashMap, HashSet};
use style::StyleRule;
use supports::SupportsRule;
Expand Down Expand Up @@ -163,6 +165,8 @@ pub enum CssRule<'i, R = DefaultAtRule> {
Property(PropertyRule<'i>),
/// A `@container` rule.
Container(ContainerRule<'i, R>),
/// A `@scope` rule.
Scope(ScopeRule<'i, R>),
/// A placeholder for a rule that was removed.
Ignored,
/// An unknown at-rule.
Expand Down Expand Up @@ -299,6 +303,10 @@ impl<'i, 'de: 'i, R: serde::Deserialize<'de>> serde::Deserialize<'de> for CssRul
let rule = ContainerRule::deserialize(deserializer)?;
Ok(CssRule::Container(rule))
}
"scope" => {
let rule = ScopeRule::deserialize(deserializer)?;
Ok(CssRule::Scope(rule))
}
"ignored" => Ok(CssRule::Ignored),
"unknown" => {
let rule = UnknownAtRule::deserialize(deserializer)?;
Expand Down Expand Up @@ -337,6 +345,7 @@ impl<'a, 'i, T: ToCss> ToCss for CssRule<'i, T> {
CssRule::LayerBlock(layer) => layer.to_css(dest),
CssRule::Property(property) => property.to_css(dest),
CssRule::Container(container) => container.to_css(dest),
CssRule::Scope(scope) => scope.to_css(dest),
CssRule::Unknown(unknown) => unknown.to_css(dest),
CssRule::Custom(rule) => rule.to_css(dest).map_err(|_| PrinterError {
kind: PrinterErrorKind::FmtError,
Expand Down Expand Up @@ -641,6 +650,7 @@ impl<'i, T> CssRuleList<'i, T> {
continue;
}
}
CssRule::Scope(scope) => scope.minify(context)?,
CssRule::Nesting(nesting) => {
if nesting.minify(context, parent_is_unused)? {
continue;
Expand Down
54 changes: 54 additions & 0 deletions src/rules/scope.rs
@@ -0,0 +1,54 @@
//! The `@scope` rule.

use super::Location;
use super::{CssRuleList, MinifyContext};
use crate::error::{MinifyError, PrinterError};
use crate::parser::DefaultAtRule;
use crate::printer::Printer;
use crate::traits::ToCss;
#[cfg(feature = "visitor")]
use crate::visitor::Visit;

/// A [@scope](https://drafts.csswg.org/css-cascade-6/#scope-atrule) rule.
///
/// @scope (<scope-start>) [to (<scope-end>)]? {
/// <stylesheet>
/// }
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct ScopeRule<'i, R = DefaultAtRule> {
// TODO: support (<scope-start>) [to (<scope-end>)]?
/// Nested rules within the `@scope` rule.
#[cfg_attr(feature = "serde", serde(borrow))]
pub rules: CssRuleList<'i, R>,
/// The location of the rule in the source file.
#[cfg_attr(feature = "visitor", skip_visit)]
pub loc: Location,
}

impl<'i, T> ScopeRule<'i, T> {
pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>) -> Result<(), MinifyError> {
self.rules.minify(context, false)
}
}

impl<'i, T: ToCss> ToCss for ScopeRule<'i, T> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
#[cfg(feature = "sourcemap")]
dest.add_mapping(self.loc);
dest.write_str("@scope")?;
dest.whitespace()?;
dest.write_char('{')?;
dest.indent();
dest.newline()?;
self.rules.to_css(dest)?;
dest.dedent();
dest.newline()?;
dest.write_char('}')
}
}

0 comments on commit 5d37f2d

Please sign in to comment.