From 95efc582b53b426574bb2b74938e7043b4c7480b Mon Sep 17 00:00:00 2001 From: connorskees Date: Wed, 11 Jan 2023 14:59:28 +0000 Subject: [PATCH] naively cache imports in certain cases --- CHANGELOG.md | 4 ++++ crates/compiler/src/evaluate/visitor.rs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f16ecce..4f23697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ --> +# 0.12.2 (unreleased) + +- implement an import cache, significantly improving the performance of certain pathological cases + # 0.12.1 - add `grass::include!` macro to make it easier to include CSS at compile time diff --git a/crates/compiler/src/evaluate/visitor.rs b/crates/compiler/src/evaluate/visitor.rs index 1844680..e9f663d 100644 --- a/crates/compiler/src/evaluate/visitor.rs +++ b/crates/compiler/src/evaluate/visitor.rs @@ -119,6 +119,11 @@ pub(crate) struct Visitor<'a> { pub map: &'a mut CodeMap, // todo: remove span_before: Span, + import_cache: BTreeMap, + /// As a simple heuristic, we don't cache the results of an import unless it + /// has been seen in the past. In the majority of cases, files are imported + /// at most once. + files_seen: BTreeSet, } impl<'a> Visitor<'a> { @@ -153,6 +158,8 @@ impl<'a> Visitor<'a> { options, span_before, map, + import_cache: BTreeMap::new(), + files_seen: BTreeSet::new(), } } @@ -822,6 +829,16 @@ impl<'a> Visitor<'a> { span: Span, ) -> SassResult { if let Some(name) = self.find_import(url.as_ref()) { + // assumption: most users use regular file paths for their imports. + // we do support importing syntactically invalid paths and paths that + // do not exist through the `Options::fs` API, so we fallback to the + // original name if necessary + let canonical = std::fs::canonicalize(&name).unwrap_or_else(|_| name.to_path_buf()); + + if let Some(style_sheet) = self.import_cache.get(&canonical) { + return Ok(style_sheet.clone()); + } + let file = self.map.add_file( name.to_string_lossy().into(), String::from_utf8(self.options.fs.read(&name)?)?, @@ -835,6 +852,13 @@ impl<'a> Visitor<'a> { self.flags .set(ContextFlags::IS_USE_ALLOWED, old_is_use_allowed); + + if self.files_seen.contains(&canonical) { + self.import_cache.insert(canonical, style_sheet.clone()); + } else { + self.files_seen.insert(canonical); + } + return Ok(style_sheet); }