2020-07-13 16:17:16 +01:00
|
|
|
use std::{ffi::OsStr, fs, path::Path, path::PathBuf};
|
2020-06-16 22:34:01 -04:00
|
|
|
|
2020-07-16 09:00:39 +01:00
|
|
|
use codemap::{Span, Spanned};
|
2020-06-16 22:34:01 -04:00
|
|
|
use peekmore::PeekMore;
|
|
|
|
|
2020-07-16 09:00:39 +01:00
|
|
|
use crate::{
|
|
|
|
common::{ListSeparator::Comma, QuoteKind},
|
|
|
|
error::SassResult,
|
|
|
|
lexer::Lexer,
|
|
|
|
value::Value,
|
|
|
|
Token,
|
|
|
|
};
|
2020-06-16 22:34:01 -04:00
|
|
|
|
|
|
|
use super::{Parser, Stmt};
|
|
|
|
|
2020-07-15 12:37:19 +01:00
|
|
|
/// Searches the current directory of the file then searches in `load_paths` directories
|
2020-07-13 18:18:25 +01:00
|
|
|
/// if the import has not yet been found.
|
2020-07-13 18:53:53 +01:00
|
|
|
/// <https://sass-lang.com/documentation/at-rules/import#finding-the-file>
|
|
|
|
/// <https://sass-lang.com/documentation/at-rules/import#load-paths>
|
2020-07-13 18:18:25 +01:00
|
|
|
fn find_import(file_path: &PathBuf, name: &OsStr, load_paths: &[&Path]) -> Option<PathBuf> {
|
|
|
|
let paths = [
|
|
|
|
file_path.with_file_name(name).with_extension("scss"),
|
|
|
|
file_path
|
|
|
|
.with_file_name(format!("_{}", name.to_str().unwrap()))
|
|
|
|
.with_extension("scss"),
|
|
|
|
file_path.clone(),
|
|
|
|
file_path.join("index.scss"),
|
|
|
|
file_path.join("_index.scss"),
|
|
|
|
];
|
|
|
|
|
|
|
|
for name in &paths {
|
|
|
|
if name.is_file() {
|
|
|
|
return Some(name.to_path_buf());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for path in load_paths {
|
|
|
|
let paths: Vec<PathBuf> = if path.is_dir() {
|
|
|
|
vec![
|
|
|
|
path.join(format!("{}.scss", name.to_str().unwrap())),
|
|
|
|
path.join(format!("_{}.scss", name.to_str().unwrap())),
|
|
|
|
path.join("index.scss"),
|
|
|
|
path.join("_index.scss"),
|
|
|
|
]
|
|
|
|
} else {
|
|
|
|
vec![
|
|
|
|
path.to_path_buf(),
|
|
|
|
path.with_file_name(name).with_extension("scss"),
|
|
|
|
path.with_file_name(format!("_{}", name.to_str().unwrap()))
|
|
|
|
.with_extension("scss"),
|
|
|
|
path.join("index.scss"),
|
|
|
|
path.join("_index.scss"),
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
for name in paths {
|
|
|
|
if name.is_file() {
|
|
|
|
return Some(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
impl<'a> Parser<'a> {
|
2020-07-16 15:34:59 +01:00
|
|
|
fn parse_single_import(&mut self, file_name: &str, span: Span) -> SassResult<Vec<Stmt>> {
|
2020-06-16 22:34:01 -04:00
|
|
|
let path: &Path = file_name.as_ref();
|
|
|
|
|
|
|
|
let path_buf = if path.is_absolute() {
|
|
|
|
// todo: test for absolute path imports
|
|
|
|
path.into()
|
|
|
|
} else {
|
|
|
|
self.path
|
|
|
|
.parent()
|
|
|
|
.unwrap_or_else(|| Path::new(""))
|
|
|
|
.join(path)
|
|
|
|
};
|
2020-07-16 15:34:59 +01:00
|
|
|
|
2020-07-06 19:47:12 -04:00
|
|
|
let name = path_buf.file_name().unwrap_or_else(|| OsStr::new(".."));
|
2020-06-17 05:43:43 -04:00
|
|
|
|
2020-07-15 12:37:19 +01:00
|
|
|
if let Some(name) = find_import(&path_buf, name, &self.options.load_paths) {
|
2020-07-13 18:18:25 +01:00
|
|
|
let file = self.map.add_file(
|
|
|
|
name.to_string_lossy().into(),
|
|
|
|
String::from_utf8(fs::read(&name)?)?,
|
|
|
|
);
|
|
|
|
return Parser {
|
|
|
|
toks: &mut Lexer::new(&file)
|
|
|
|
.collect::<Vec<Token>>()
|
|
|
|
.into_iter()
|
|
|
|
.peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: &name,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.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,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-13 14:52:52 +01:00
|
|
|
}
|
2020-07-13 18:18:25 +01:00
|
|
|
.parse();
|
2020-07-13 14:52:52 +01:00
|
|
|
}
|
2020-07-16 09:00:39 +01:00
|
|
|
self.whitespace();
|
2020-07-13 14:52:52 +01:00
|
|
|
|
2020-07-06 19:47:12 -04:00
|
|
|
Err(("Can't find stylesheet to import.", span).into())
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|
2020-07-16 15:34:59 +01:00
|
|
|
|
2020-07-16 09:00:39 +01:00
|
|
|
pub(super) fn import(&mut self) -> SassResult<Vec<Stmt>> {
|
|
|
|
self.whitespace();
|
2020-07-16 15:34:59 +01:00
|
|
|
|
2020-07-16 09:00:39 +01:00
|
|
|
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,
|
2020-07-28 18:09:23 -04:00
|
|
|
} = self.parse_value(true, &|_| false)?;
|
2020-07-16 09:00:39 +01:00
|
|
|
|
|
|
|
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 {
|
2020-07-16 15:34:59 +01:00
|
|
|
self.parse_single_import(&s, span)
|
2020-07-16 09:00:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Value::String(s, QuoteKind::None) => {
|
|
|
|
if s.starts_with("url(") {
|
|
|
|
Ok(vec![Stmt::Import(s)])
|
|
|
|
} else {
|
2020-07-16 15:34:59 +01:00
|
|
|
self.parse_single_import(&s, span)
|
2020-07-16 09:00:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
2020-07-16 15:34:59 +01:00
|
|
|
list_of_imports.append(&mut self.parse_single_import(&s, span)?);
|
2020-07-16 09:00:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Value::String(s, QuoteKind::None) => {
|
|
|
|
if s.starts_with("url(") {
|
|
|
|
list_of_imports.push(Stmt::Import(s));
|
|
|
|
} else {
|
2020-07-16 15:34:59 +01:00
|
|
|
list_of_imports.append(&mut self.parse_single_import(&s, span)?);
|
2020-07-16 09:00:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return Err(("Expected string.", span).into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(list_of_imports)
|
|
|
|
}
|
|
|
|
_ => Err(("Expected string.", span).into()),
|
|
|
|
}
|
|
|
|
}
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|