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 super::{Builtin, GlobalFunctionMap, GLOBAL_FUNCTIONS};
|
||||||
|
|
||||||
|
use codemap::Spanned;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
args::CallArgs,
|
args::CallArgs,
|
||||||
common::{Identifier, QuoteKind},
|
common::{Identifier, QuoteKind},
|
||||||
@ -220,7 +222,10 @@ pub(crate) fn get_function(mut args: CallArgs, parser: &mut Parser<'_>) -> SassR
|
|||||||
parser
|
parser
|
||||||
.modules
|
.modules
|
||||||
.get(module_name.into(), args.span())?
|
.get(module_name.into(), args.span())?
|
||||||
.get_fn(name)
|
.get_fn(Spanned {
|
||||||
|
node: name,
|
||||||
|
span: args.span(),
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
parser.scopes.get_fn(name, parser.global_scope)
|
parser.scopes.get_fn(name, parser.global_scope)
|
||||||
} {
|
} {
|
||||||
|
@ -51,6 +51,14 @@ impl Modules {
|
|||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
pub fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
|
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) {
|
match self.0.vars.get(&name.node) {
|
||||||
Some(v) => Ok(v),
|
Some(v) => Ok(v),
|
||||||
None => Err(("Undefined variable.", name.span).into()),
|
None => Err(("Undefined variable.", name.span).into()),
|
||||||
@ -61,16 +69,24 @@ impl Module {
|
|||||||
self.0.vars.insert(name.into(), value);
|
self.0.vars.insert(name.into(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fn(&self, name: Identifier) -> Option<SassFunction> {
|
pub fn get_fn(&self, name: Spanned<Identifier>) -> SassResult<Option<SassFunction>> {
|
||||||
self.0.functions.get(&name).cloned()
|
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 {
|
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 {
|
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(
|
pub fn insert_builtin(
|
||||||
@ -89,6 +105,7 @@ impl Module {
|
|||||||
self.0
|
self.0
|
||||||
.functions
|
.functions
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|(key, _)| !key.as_str().starts_with('-'))
|
||||||
.map(|(key, value)| {
|
.map(|(key, value)| {
|
||||||
(
|
(
|
||||||
Value::String(key.to_string(), QuoteKind::Quoted),
|
Value::String(key.to_string(), QuoteKind::Quoted),
|
||||||
@ -104,6 +121,7 @@ impl Module {
|
|||||||
self.0
|
self.0
|
||||||
.vars
|
.vars
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|(key, _)| !key.as_str().starts_with('-'))
|
||||||
.map(|(key, value)| {
|
.map(|(key, value)| {
|
||||||
(
|
(
|
||||||
Value::String(key.to_string(), QuoteKind::Quoted),
|
Value::String(key.to_string(), QuoteKind::Quoted),
|
||||||
|
@ -271,7 +271,7 @@ impl<'a> Parser<'a> {
|
|||||||
let function = self
|
let function = self
|
||||||
.modules
|
.modules
|
||||||
.get(module.into(), module_span)?
|
.get(module.into(), module_span)?
|
||||||
.get_fn(fn_name.node)
|
.get_fn(fn_name)?
|
||||||
.ok_or(("Undefined function.", fn_name.span))?;
|
.ok_or(("Undefined function.", fn_name.span))?;
|
||||||
|
|
||||||
if !matches!(self.toks.next(), Some(Token { kind: '(', .. })) {
|
if !matches!(self.toks.next(), Some(Token { kind: '(', .. })) {
|
||||||
|
@ -26,17 +26,8 @@ fn import_no_semicolon() {
|
|||||||
fn import_no_quotes() {
|
fn import_no_quotes() {
|
||||||
let input = "@import import_no_quotes";
|
let input = "@import import_no_quotes";
|
||||||
tempfile!("import_no_quotes", "$a: red;");
|
tempfile!("import_no_quotes", "$a: red;");
|
||||||
match grass::from_string(input.to_string(), &grass::Options::default()) {
|
|
||||||
Ok(..) => panic!("did not fail"),
|
assert_err!("Error: Expected string.", input);
|
||||||
Err(e) => assert_eq!(
|
|
||||||
"Error: Expected string.",
|
|
||||||
e.to_string()
|
|
||||||
.chars()
|
|
||||||
.take_while(|c| *c != '\n')
|
|
||||||
.collect::<String>()
|
|
||||||
.as_str()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -85,3 +85,20 @@ macro_rules! tempfile {
|
|||||||
write!(f, "{}", $content).unwrap();
|
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]
|
#[test]
|
||||||
fn use_user_defined_as() {
|
fn use_user_defined_as() {
|
||||||
let input = "@use \"use_user_defined_as\" as module;\na {\n color: module.$a;\n}";
|
let input = "@use \"use_user_defined_as\" as module;\na {\n color: module.$a;\n}";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user