added comma separated imports

This commit is contained in:
Joe Ling - uni laptop 2020-07-16 09:00:39 +01:00 committed by Connor Skees
parent 741f560e90
commit be4d02490c
2 changed files with 91 additions and 39 deletions

View File

@ -1,9 +1,15 @@
use std::{ffi::OsStr, fs, path::Path, path::PathBuf}; use std::{ffi::OsStr, fs, path::Path, path::PathBuf};
use codemap::Spanned; use codemap::{Span, Spanned};
use peekmore::PeekMore; use peekmore::PeekMore;
use crate::{common::QuoteKind, error::SassResult, lexer::Lexer, value::Value, Token}; use crate::{
common::{ListSeparator::Comma, QuoteKind},
error::SassResult,
lexer::Lexer,
value::Value,
Token,
};
use super::{Parser, Stmt}; use super::{Parser, Stmt};
@ -58,41 +64,7 @@ fn find_import(file_path: &PathBuf, name: &OsStr, load_paths: &[&Path]) -> Optio
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub(super) fn import(&mut self) -> SassResult<Vec<Stmt>> { pub fn _parse_single_import(&mut self, file_name: &str, span: Span) -> SassResult<Vec<Stmt>> {
self.whitespace();
match self.toks.peek() {
Some(Token { kind: '\'', .. })
| Some(Token { kind: '"', .. })
| Some(Token { kind: 'u', .. }) => {}
Some(Token { pos, .. }) => return Err(("Expected string.", *pos).into()),
None => return Err(("expected more input.", self.span_before).into()),
};
let Spanned {
node: file_name_as_value,
span,
} = self.parse_value(true)?;
let file_name = match file_name_as_value {
Value::String(s, QuoteKind::Quoted) => {
if s.ends_with(".css") || s.starts_with("http://") || s.starts_with("https://") {
return Ok(vec![Stmt::Import(format!("\"{}\"", s))]);
} else {
s
}
}
Value::String(s, QuoteKind::None) => {
if s.starts_with("url(") {
return Ok(vec![Stmt::Import(s)]);
} else {
s
}
}
_ => return Err(("Expected string.", span).into()),
};
self.whitespace();
let path: &Path = file_name.as_ref(); let path: &Path = file_name.as_ref();
let path_buf = if path.is_absolute() { let path_buf = if path.is_absolute() {
@ -104,7 +76,6 @@ impl<'a> Parser<'a> {
.unwrap_or_else(|| Path::new("")) .unwrap_or_else(|| Path::new(""))
.join(path) .join(path)
}; };
let name = path_buf.file_name().unwrap_or_else(|| OsStr::new("..")); let name = path_buf.file_name().unwrap_or_else(|| OsStr::new(".."));
if let Some(name) = find_import(&path_buf, name, &self.options.load_paths) { if let Some(name) = find_import(&path_buf, name, &self.options.load_paths) {
@ -112,7 +83,6 @@ impl<'a> Parser<'a> {
name.to_string_lossy().into(), name.to_string_lossy().into(),
String::from_utf8(fs::read(&name)?)?, String::from_utf8(fs::read(&name)?)?,
); );
return Parser { return Parser {
toks: &mut Lexer::new(&file) toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>() .collect::<Vec<Token>>()
@ -134,7 +104,67 @@ impl<'a> Parser<'a> {
} }
.parse(); .parse();
} }
self.whitespace();
Err(("Can't find stylesheet to import.", span).into()) Err(("Can't find stylesheet to import.", span).into())
} }
pub(super) fn import(&mut self) -> SassResult<Vec<Stmt>> {
self.whitespace();
match self.toks.peek() {
Some(Token { kind: '\'', .. })
| Some(Token { kind: '"', .. })
| Some(Token { kind: 'u', .. }) => {}
Some(Token { pos, .. }) => return Err(("Expected string.", *pos).into()),
None => return Err(("expected more input.", self.span_before).into()),
};
let Spanned {
node: file_name_as_value,
span,
} = self.parse_value(true)?;
match file_name_as_value {
Value::String(s, QuoteKind::Quoted) => {
if s.ends_with(".css") || s.starts_with("http://") || s.starts_with("https://") {
Ok(vec![Stmt::Import(format!("\"{}\"", s))])
} else {
self._parse_single_import(&s, span)
}
}
Value::String(s, QuoteKind::None) => {
if s.starts_with("url(") {
Ok(vec![Stmt::Import(s)])
} else {
self._parse_single_import(&s, span)
}
}
Value::List(v, Comma, _) => {
let mut list_of_imports: Vec<Stmt> = Vec::new();
for file_name_element in v {
match file_name_element {
Value::String(s, QuoteKind::Quoted) => {
if s.ends_with(".css")
|| s.starts_with("http://")
|| s.starts_with("https://")
{
list_of_imports.push(Stmt::Import(format!("\"{}\"", s)));
} else {
list_of_imports.append(&mut self._parse_single_import(&s, span)?);
}
}
Value::String(s, QuoteKind::None) => {
if s.starts_with("url(") {
list_of_imports.push(Stmt::Import(s));
} else {
list_of_imports.append(&mut self._parse_single_import(&s, span)?);
}
}
_ => return Err(("Expected string.", span).into()),
}
}
Ok(list_of_imports)
}
_ => Err(("Expected string.", span).into()),
}
}
} }

View File

@ -82,6 +82,28 @@ fn single_quotes_import() {
); );
} }
#[test]
fn comma_seperated_import() {
let input = "@import 'firsta', 'seconda';\na {\n color: $a;\n}";
tempfile!("firsta", "$a: red;");
tempfile!("seconda", "p { color: blue; }");
assert_eq!(
"p {\n color: blue;\n}\n\na {\n color: red;\n}\n",
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
);
}
#[test]
fn comma_seperated_import_order() {
let input = "@import 'firstb', 'secondb', url(third);";
tempfile!("firstb", "p { color: red; }");
tempfile!("secondb", "p { color: blue; }");
assert_eq!(
"p {\n color: red;\n}\n\np {\n color: blue;\n}\n@import url(third);\n",
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
);
}
#[test] #[test]
fn finds_name_scss() { fn finds_name_scss() {
let input = "@import \"finds_name_scss\";\na {\n color: $a;\n}"; let input = "@import \"finds_name_scss\";\na {\n color: $a;\n}";