From cc881db2545cf6bcb289cba2a20376389517c66b Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 24 Apr 2020 21:38:25 -0400 Subject: [PATCH] more robustly handle import paths --- src/imports.rs | 7 +++++-- src/lib.rs | 9 +++++++-- tests/imports.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index 7673254..c103639 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -7,10 +7,13 @@ use crate::error::SassResult; use crate::scope::Scope; use crate::{Stmt, StyleSheet}; -pub(crate) fn import>(path: P) -> SassResult<(Vec>, Scope)> { +pub(crate) fn import(ctx: &Path, path: &Path) -> SassResult<(Vec>, Scope)> { let mut rules = Vec::new(); let mut scope = Scope::new(); - let path_buf = path.as_ref().to_path_buf(); + if path.is_absolute() { + todo!("absolute import") + } + let path_buf = ctx.parent().unwrap_or(Path::new("")).join(path); let name = path_buf.file_name().expect("todo! path ended in `..`"); if path_buf.extension() == Some(OsStr::new(".css")) { // || name.starts_with("http://") || name.starts_with("https://") { diff --git a/src/lib.rs b/src/lib.rs index 8000eb2..cbfc4ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,7 @@ impl StyleSheet { lexer: Lexer::new(&file).peekmore(), nesting: 0, map: &map, + path: Path::new(""), } .parse_toplevel() .map_err(|e| raw_to_parse_error(&map, e).to_string())? @@ -237,6 +238,7 @@ impl StyleSheet { lexer: Lexer::new(&file).peekmore(), nesting: 0, map: &map, + path: Path::new(""), } .parse_toplevel() .map_err(|e| raw_to_parse_error(&map, e))? @@ -261,12 +263,13 @@ impl StyleSheet { #[cfg(not(feature = "wasm"))] pub fn from_path + Into + Clone>(p: P) -> SassResult { let mut map = CodeMap::new(); - let file = map.add_file(p.clone().into(), String::from_utf8(fs::read(p)?)?); + let file = map.add_file(p.clone().into(), String::from_utf8(fs::read(p.clone())?)?); Css::from_stylesheet(StyleSheet( StyleSheetParser { lexer: Lexer::new(&file).peekmore(), nesting: 0, map: &map, + path: p.as_ref(), } .parse_toplevel() .map_err(|e| raw_to_parse_error(&map, e))? @@ -286,6 +289,7 @@ impl StyleSheet { lexer: Lexer::new(&file).peekmore(), nesting: 0, map: &map, + path: p.as_ref(), } .parse_toplevel()?) } @@ -299,6 +303,7 @@ struct StyleSheetParser<'a> { lexer: PeekMoreIterator>, nesting: u32, map: &'a CodeMap, + path: &'a Path, } impl<'a> StyleSheetParser<'a> { @@ -382,7 +387,7 @@ impl<'a> StyleSheetParser<'a> { devour_whitespace(&mut self.lexer); - let (new_rules, new_scope) = import(file_name)?; + let (new_rules, new_scope) = import(self.path, file_name.as_ref())?; rules.extend(new_rules); GLOBAL_SCOPE.with(|s| { s.borrow_mut().extend(new_scope); diff --git a/tests/imports.rs b/tests/imports.rs index 88dcbf2..0db9fc5 100644 --- a/tests/imports.rs +++ b/tests/imports.rs @@ -19,6 +19,21 @@ macro_rules! tempfile { .unwrap(); write!(f, "{}", $content).unwrap(); }; + ($name:literal, $content:literal, dir=$dir:literal) => { + let _d = Builder::new() + .rand_bytes(0) + .prefix("") + .suffix($dir) + .tempdir_in("") + .unwrap(); + let mut f = dbg!(Builder::new() + .rand_bytes(0) + .prefix("") + .suffix($name) + .tempfile_in($dir) + .unwrap()); + write!(f, "{}", $content).unwrap(); + }; } #[test] @@ -99,3 +114,24 @@ fn chained_imports() { &StyleSheet::new(input.to_string()).expect(input) ); } + +#[test] +fn chained_imports_in_directory() { + let input = "@import \"chained_imports_in_directory__a\";\na {\n color: $a;\n}"; + tempfile!( + "chained_imports_in_directory__a.scss", + "@import \"chained_imports_in_directory__b\";" + ); + tempfile!( + "index.scss", + "@import \"../chained_imports_in_directory__c\";", + dir = "chained_imports_in_directory__b" + ); + tempfile!("chained_imports_in_directory__c.scss", "$a: red;"); + assert_eq!( + "a {\n color: red;\n}\n", + &StyleSheet::new(input.to_string()).expect(input) + ); +} + +// todo: test for calling paths, e.g. `grass b\index.scss`