grass/src/parse/import.rs

184 lines
6.4 KiB
Rust
Raw Normal View History

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};
impl<'a> Parser<'a> {
2020-07-30 17:21:32 -04:00
/// Searches the current directory of the file then searches in `load_paths` directories
/// if the import has not yet been found.
///
/// <https://sass-lang.com/documentation/at-rules/import#finding-the-file>
/// <https://sass-lang.com/documentation/at-rules/import#load-paths>
pub(super) fn find_import(&self, path: &Path) -> Option<PathBuf> {
2020-06-16 22:34:01 -04:00
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
let name = path_buf.file_name().unwrap_or_else(|| OsStr::new(".."));
2020-07-30 17:21:32 -04:00
let paths = [
path_buf.with_file_name(name).with_extension("scss"),
path_buf
.with_file_name(format!("_{}", name.to_str().unwrap()))
.with_extension("scss"),
path_buf.clone(),
path_buf.join("index.scss"),
path_buf.join("_index.scss"),
];
for name in &paths {
if name.is_file() {
return Some(name.to_path_buf());
}
}
for path in &self.options.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
}
pub(crate) fn parse_single_import(
&mut self,
file_name: &str,
span: Span,
) -> SassResult<Vec<Stmt>> {
2020-07-30 17:21:32 -04:00
let path: &Path = file_name.as_ref();
if let Some(name) = self.find_import(path) {
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,
options: self.options,
2020-07-25 19:22:12 -04:00
modules: self.modules,
module_config: self.module_config,
2020-07-13 14:52:52 +01:00
}
.parse();
2020-07-13 14:52:52 +01: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>> {
2020-08-02 23:00:00 -04:00
if self.flags.in_function() {
return Err(("This at-rule is not allowed here.", self.span_before).into());
}
2020-07-16 09:00:39 +01:00
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,
} = 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
}