Add custom global variables

This commit is contained in:
Shadowfacts 2025-01-02 12:52:20 -05:00
parent 692dc39f55
commit e64e648b61
3 changed files with 49 additions and 2 deletions

View File

@ -2566,7 +2566,7 @@ impl<'a> Visitor<'a> {
AstExpr::Paren(expr) => self.visit_expr((*expr).clone())?, AstExpr::Paren(expr) => self.visit_expr((*expr).clone())?,
AstExpr::ParentSelector => self.visit_parent_selector(), AstExpr::ParentSelector => self.visit_parent_selector(),
AstExpr::UnaryOp(op, expr, span) => self.visit_unary_op(op, (*expr).clone(), span)?, AstExpr::UnaryOp(op, expr, span) => self.visit_unary_op(op, (*expr).clone(), span)?,
AstExpr::Variable { name, namespace } => self.env.get_var(name, namespace)?, AstExpr::Variable { name, namespace } => self.visit_variable(name, namespace)?,
AstExpr::Supports(condition) => Value::String( AstExpr::Supports(condition) => Value::String(
self.visit_supports_condition((*condition).clone())?, self.visit_supports_condition((*condition).clone())?,
QuoteKind::None, QuoteKind::None,
@ -3079,4 +3079,22 @@ impl<'a> Visitor<'a> {
Ok(None) Ok(None)
} }
fn visit_variable(
&mut self,
name: Spanned<Identifier>,
namespace: Option<Spanned<Identifier>>,
) -> SassResult<Value> {
self.env.get_var(name, namespace).or_else(|e| {
if namespace.is_none() {
if let Some(v) = self.options.custom_vars.get(name.as_str()) {
Ok(v.clone())
} else {
Err(e)
}
} else {
Err(e)
}
})
}
} }

View File

@ -3,7 +3,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use crate::{builtin::Builtin, Fs, Logger, StdFs, StdLogger}; use crate::{builtin::Builtin, value::Value, Fs, Logger, StdFs, StdLogger};
/// Configuration for Sass compilation /// Configuration for Sass compilation
/// ///
@ -20,6 +20,7 @@ pub struct Options<'a> {
pub(crate) quiet: bool, pub(crate) quiet: bool,
pub(crate) input_syntax: Option<InputSyntax>, pub(crate) input_syntax: Option<InputSyntax>,
pub(crate) custom_fns: HashMap<String, Builtin>, pub(crate) custom_fns: HashMap<String, Builtin>,
pub(crate) custom_vars: HashMap<String, Value>,
} }
impl Default for Options<'_> { impl Default for Options<'_> {
@ -35,6 +36,7 @@ impl Default for Options<'_> {
quiet: false, quiet: false,
input_syntax: None, input_syntax: None,
custom_fns: HashMap::new(), custom_fns: HashMap::new(),
custom_vars: HashMap::new(),
} }
} }
} }
@ -177,6 +179,13 @@ impl<'a> Options<'a> {
self self
} }
#[must_use]
#[inline]
pub fn add_custom_var<S: Into<String>>(mut self, name: S, value: Value) -> Self {
self.custom_vars.insert(name.into(), value);
self
}
pub(crate) fn is_compressed(&self) -> bool { pub(crate) fn is_compressed(&self) -> bool {
matches!(self.style, OutputStyle::Compressed) matches!(self.style, OutputStyle::Compressed)
} }

View File

@ -0,0 +1,20 @@
use grass::{from_string, Options};
use grass_compiler::sass_value::{QuoteKind, Value};
#[test]
fn lookup_custom_global_variable() {
let opts = Options::default().add_custom_var("x", Value::String("foo".into(), QuoteKind::None));
assert_eq!(
from_string("a {\n test: $x;\n}\n", &opts).ok(),
Some("a {\n test: foo;\n}\n".to_owned())
);
}
#[test]
fn user_defined_takes_precedence_over_global_variable() {
let opts = Options::default().add_custom_var("x", Value::String("foo".into(), QuoteKind::None));
assert_eq!(
from_string("$x: bar;\na {\n test: $x;\n}\n", &opts).ok(),
Some("a {\n test: bar;\n}\n".to_owned())
);
}