From cacf605af8b9b3661a7778f768fb100251504780 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Thu, 6 Aug 2020 21:58:53 -0400 Subject: [PATCH] respect `$with` argument to `load-css` --- src/builtin/modules/meta.rs | 50 ++++++++++++---- src/parse/mod.rs | 114 ++++++++++++++++++++---------------- 2 files changed, 100 insertions(+), 64 deletions(-) diff --git a/src/builtin/modules/meta.rs b/src/builtin/modules/meta.rs index e808be7..fb0b1ab 100644 --- a/src/builtin/modules/meta.rs +++ b/src/builtin/modules/meta.rs @@ -1,3 +1,5 @@ +use codemap::Spanned; + use crate::{ args::CallArgs, builtin::{ @@ -5,7 +7,7 @@ use crate::{ call, content_exists, feature_exists, function_exists, get_function, global_variable_exists, inspect, keywords, mixin_exists, type_of, variable_exists, }, - modules::Module, + modules::{Module, ModuleConfig}, }, error::SassResult, parse::{Parser, Stmt}, @@ -15,13 +17,15 @@ use crate::{ fn load_css(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult> { args.max_args(2)?; + let span = args.span(); + // todo: https://github.com/sass/dart-sass/issues/1054 let url = match args.get_err(0, "module")? { Value::String(s, ..) => s, v => { return Err(( - format!("$module: {} is not a string.", v.inspect(args.span())?), - args.span(), + format!("$module: {} is not a string.", v.inspect(span)?), + span, ) .into()) } @@ -30,19 +34,39 @@ fn load_css(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult let with = match args.default_arg(1, "with", Value::Null)? { Value::Map(map) => Some(map), Value::Null => None, - v => { - return Err(( - format!("$with: {} is not a map.", v.inspect(args.span())?), - args.span(), - ) - .into()) - } + v => return Err((format!("$with: {} is not a map.", v.inspect(span)?), span).into()), }; - if let Some(..) = with { - todo!("`$with` to `load-css` not yet implemented") + // todo: tests for `with` + if let Some(with) = with { + let mut config = ModuleConfig::default(); + + for (key, value) in with { + let key = match key { + Value::String(s, ..) => s, + v => { + return Err(( + format!("$with key: {} is not a string.", v.inspect(span)?), + span, + ) + .into()) + } + }; + + config.insert( + Spanned { + node: key.into(), + span, + }, + value.span(span), + )?; + } + + let (_, stmts) = parser.load_module(&url, &mut config)?; + + Ok(stmts) } else { - parser.parse_single_import(&url, args.span()) + parser.parse_single_import(&url, span) } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 418ad7b..79db038 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -208,6 +208,66 @@ impl<'a> Parser<'a> { Ok(config) } + pub fn load_module( + &mut self, + name: &str, + config: &mut ModuleConfig, + ) -> SassResult<(Module, Vec)> { + Ok(match name { + "sass:color" => (declare_module_color(), Vec::new()), + "sass:list" => (declare_module_list(), Vec::new()), + "sass:map" => (declare_module_map(), Vec::new()), + "sass:math" => (declare_module_math(), Vec::new()), + "sass:meta" => (declare_module_meta(), Vec::new()), + "sass:selector" => (declare_module_selector(), Vec::new()), + "sass:string" => (declare_module_string(), Vec::new()), + _ => { + if let Some(import) = self.find_import(name.as_ref()) { + let mut global_scope = Scope::new(); + + let file = self + .map + .add_file(name.to_owned(), String::from_utf8(fs::read(&import)?)?); + + let stmts = Parser { + toks: &mut Lexer::new(&file) + .collect::>() + .into_iter() + .peekmore(), + map: self.map, + path: &import, + scopes: self.scopes, + global_scope: &mut global_scope, + super_selectors: self.super_selectors, + span_before: file.span.subspan(0, 0), + content: self.content, + flags: self.flags, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + content_scopes: self.content_scopes, + options: self.options, + modules: self.modules, + module_config: config, + } + .parse()?; + + if !config.is_empty() { + return Err(( + "This variable was not declared with !default in the @used module.", + self.span_before, + ) + .into()); + } + + (Module::new_from_scope(global_scope), stmts) + } else { + return Err(("Can't find stylesheet to import.", self.span_before).into()); + } + } + }) + } + /// Returns any multiline comments that may have been found /// while loading modules #[allow(clippy::eval_order_dependence)] @@ -260,58 +320,10 @@ impl<'a> Parser<'a> { self.whitespace_or_comment(); self.expect_char(';')?; - let module = match module_name.as_ref() { - "sass:color" => declare_module_color(), - "sass:list" => declare_module_list(), - "sass:map" => declare_module_map(), - "sass:math" => declare_module_math(), - "sass:meta" => declare_module_meta(), - "sass:selector" => declare_module_selector(), - "sass:string" => declare_module_string(), - _ => { - if let Some(import) = self.find_import(module_name.as_ref().as_ref()) { - let mut global_scope = Scope::new(); + let (module, mut stmts) = + self.load_module(module_name.as_ref(), &mut config)?; - let file = self.map.add_file( - module_name.clone().into_owned(), - String::from_utf8(fs::read(&import)?)?, - ); - - comments.append( - &mut Parser { - toks: &mut Lexer::new(&file) - .collect::>() - .into_iter() - .peekmore(), - map: self.map, - path: &import, - scopes: self.scopes, - global_scope: &mut global_scope, - super_selectors: self.super_selectors, - span_before: file.span.subspan(0, 0), - content: self.content, - flags: self.flags, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - content_scopes: self.content_scopes, - options: self.options, - modules: self.modules, - module_config: &mut config, - } - .parse()?, - ); - - if !config.is_empty() { - return Err(("This variable was not declared with !default in the @used module.", span).into()); - } - - Module::new_from_scope(global_scope) - } else { - return Err(("Can't find stylesheet to import.", span).into()); - } - } - }; + comments.append(&mut stmts); // if the config isn't empty here, that means // variables were passed to a builtin module