initial implementation of private module members
This commit is contained in:
parent
a9e4d5cba5
commit
698339b8c7
@ -1,5 +1,7 @@
|
||||
use super::{Builtin, GlobalFunctionMap, GLOBAL_FUNCTIONS};
|
||||
|
||||
use codemap::Spanned;
|
||||
|
||||
use crate::{
|
||||
args::CallArgs,
|
||||
common::{Identifier, QuoteKind},
|
||||
@ -220,7 +222,10 @@ pub(crate) fn get_function(mut args: CallArgs, parser: &mut Parser<'_>) -> SassR
|
||||
parser
|
||||
.modules
|
||||
.get(module_name.into(), args.span())?
|
||||
.get_fn(name)
|
||||
.get_fn(Spanned {
|
||||
node: name,
|
||||
span: args.span(),
|
||||
})?
|
||||
} else {
|
||||
parser.scopes.get_fn(name, parser.global_scope)
|
||||
} {
|
||||
|
@ -51,6 +51,14 @@ impl Modules {
|
||||
|
||||
impl Module {
|
||||
pub fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
|
||||
if name.node.as_str().starts_with('-') {
|
||||
return Err((
|
||||
"Private members can't be accessed from outside their modules.",
|
||||
name.span,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
match self.0.vars.get(&name.node) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(("Undefined variable.", name.span).into()),
|
||||
@ -61,16 +69,24 @@ impl Module {
|
||||
self.0.vars.insert(name.into(), value);
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, name: Identifier) -> Option<SassFunction> {
|
||||
self.0.functions.get(&name).cloned()
|
||||
pub fn get_fn(&self, name: Spanned<Identifier>) -> SassResult<Option<SassFunction>> {
|
||||
if name.node.as_str().starts_with('-') {
|
||||
return Err((
|
||||
"Private members can't be accessed from outside their modules.",
|
||||
name.span,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(self.0.functions.get(&name.node).cloned())
|
||||
}
|
||||
|
||||
pub fn var_exists(&self, name: Identifier) -> bool {
|
||||
self.0.var_exists(name)
|
||||
!name.as_str().starts_with('-') && self.0.var_exists(name)
|
||||
}
|
||||
|
||||
pub fn mixin_exists(&self, name: Identifier) -> bool {
|
||||
self.0.mixin_exists(name)
|
||||
!name.as_str().starts_with('-') && self.0.mixin_exists(name)
|
||||
}
|
||||
|
||||
pub fn insert_builtin(
|
||||
@ -89,6 +105,7 @@ impl Module {
|
||||
self.0
|
||||
.functions
|
||||
.iter()
|
||||
.filter(|(key, _)| !key.as_str().starts_with('-'))
|
||||
.map(|(key, value)| {
|
||||
(
|
||||
Value::String(key.to_string(), QuoteKind::Quoted),
|
||||
@ -104,6 +121,7 @@ impl Module {
|
||||
self.0
|
||||
.vars
|
||||
.iter()
|
||||
.filter(|(key, _)| !key.as_str().starts_with('-'))
|
||||
.map(|(key, value)| {
|
||||
(
|
||||
Value::String(key.to_string(), QuoteKind::Quoted),
|
||||
|
@ -271,7 +271,7 @@ impl<'a> Parser<'a> {
|
||||
let function = self
|
||||
.modules
|
||||
.get(module.into(), module_span)?
|
||||
.get_fn(fn_name.node)
|
||||
.get_fn(fn_name)?
|
||||
.ok_or(("Undefined function.", fn_name.span))?;
|
||||
|
||||
if !matches!(self.toks.next(), Some(Token { kind: '(', .. })) {
|
||||
|
@ -26,17 +26,8 @@ fn import_no_semicolon() {
|
||||
fn import_no_quotes() {
|
||||
let input = "@import import_no_quotes";
|
||||
tempfile!("import_no_quotes", "$a: red;");
|
||||
match grass::from_string(input.to_string(), &grass::Options::default()) {
|
||||
Ok(..) => panic!("did not fail"),
|
||||
Err(e) => assert_eq!(
|
||||
"Error: Expected string.",
|
||||
e.to_string()
|
||||
.chars()
|
||||
.take_while(|c| *c != '\n')
|
||||
.collect::<String>()
|
||||
.as_str()
|
||||
),
|
||||
}
|
||||
|
||||
assert_err!("Error: Expected string.", input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -85,3 +85,20 @@ macro_rules! tempfile {
|
||||
write!(f, "{}", $content).unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_err {
|
||||
($err:literal, $input:expr) => {
|
||||
match grass::from_string($input.to_string(), &grass::Options::default()) {
|
||||
Ok(..) => panic!("did not fail"),
|
||||
Err(e) => assert_eq!(
|
||||
$err,
|
||||
e.to_string()
|
||||
.chars()
|
||||
.take_while(|c| *c != '\n')
|
||||
.collect::<String>()
|
||||
.as_str()
|
||||
),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
62
tests/use.rs
62
tests/use.rs
@ -74,6 +74,68 @@ fn use_user_defined_same_directory() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_variable_begins_with_underscore() {
|
||||
let input = "@use \"private_variable_begins_with_underscore\" as module;\na {\n color: module.$_foo;\n}";
|
||||
tempfile!(
|
||||
"private_variable_begins_with_underscore.scss",
|
||||
"$_foo: red; a { color: $_foo; }"
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
"Error: Private members can't be accessed from outside their modules.",
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_variable_begins_with_hyphen() {
|
||||
let input =
|
||||
"@use \"private_variable_begins_with_hyphen\" as module;\na {\n color: module.$-foo;\n}";
|
||||
tempfile!(
|
||||
"private_variable_begins_with_hyphen.scss",
|
||||
"$-foo: red; a { color: $-foo; }"
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
"Error: Private members can't be accessed from outside their modules.",
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_function() {
|
||||
let input = "@use \"private_function\" as module;\na {\n color: module._foo(green);\n}";
|
||||
tempfile!(
|
||||
"private_function.scss",
|
||||
"@function _foo($a) { @return $a; } a { color: _foo(red); }"
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
"Error: Private members can't be accessed from outside their modules.",
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_variable_exists_private() {
|
||||
let input = r#"
|
||||
@use "global_variable_exists_private" as module;
|
||||
a {
|
||||
color: global-variable-exists($name: foo, $module: module);
|
||||
color: global-variable-exists($name: _foo, $module: module);
|
||||
}"#;
|
||||
tempfile!(
|
||||
"global_variable_exists_private.scss",
|
||||
"$foo: red;\n$_foo: red;\n"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"a {\n color: true;\n color: false;\n}\n",
|
||||
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_user_defined_as() {
|
||||
let input = "@use \"use_user_defined_as\" as module;\na {\n color: module.$a;\n}";
|
||||
|
Loading…
x
Reference in New Issue
Block a user