Skip to content

Commit fe9f52e

Browse files
committed
fix(oxide): preserve symlink paths for source ignores
1 parent d03edef commit fe9f52e

2 files changed

Lines changed: 49 additions & 5 deletions

File tree

crates/oxide/src/scanner/sources.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::glob::split_pattern;
22
use crate::GlobEntry;
33
use bexpand::Expression;
4-
use std::path::PathBuf;
4+
use std::path::{Component, Path, PathBuf};
55
use tracing::{event, Level};
66

77
use super::auto_source_detection::IGNORED_CONTENT_DIRS;
@@ -116,12 +116,12 @@ impl PublicSourceEntry {
116116
PathBuf::from(&self.base).join(&self.pattern)
117117
};
118118

119-
match dunce::canonicalize(combined_path) {
120-
Ok(resolved_path) if resolved_path.is_dir() => {
119+
match resolve_path(&combined_path, self.negated) {
120+
Ok(resolved_path) if combined_path.is_dir() => {
121121
self.base = resolved_path.to_string_lossy().to_string();
122122
self.pattern = "**/*".to_owned();
123123
}
124-
Ok(resolved_path) if resolved_path.is_file() => {
124+
Ok(resolved_path) if combined_path.is_file() => {
125125
self.base = resolved_path
126126
.parent()
127127
.unwrap()
@@ -144,7 +144,7 @@ impl PublicSourceEntry {
144144
Some(static_part) => {
145145
// TODO: If the base does not exist on disk, try removing the last slash and try
146146
// again.
147-
match dunce::canonicalize(base.join(static_part)) {
147+
match resolve_path(&base.join(static_part), self.negated) {
148148
Ok(base) => base,
149149
Err(err) => {
150150
event!(tracing::Level::ERROR, "Failed to resolve glob: {:?}", err);
@@ -171,6 +171,31 @@ impl PublicSourceEntry {
171171
}
172172
}
173173

174+
fn resolve_path(path: &Path, preserve_symlinks: bool) -> std::io::Result<PathBuf> {
175+
if preserve_symlinks {
176+
path.metadata()?;
177+
return Ok(normalize_path_lexically(path));
178+
}
179+
180+
dunce::canonicalize(path)
181+
}
182+
183+
fn normalize_path_lexically(path: &Path) -> PathBuf {
184+
let mut normalized = PathBuf::new();
185+
186+
for component in path.components() {
187+
match component {
188+
Component::CurDir => {}
189+
Component::ParentDir => {
190+
normalized.pop();
191+
}
192+
component => normalized.push(component.as_os_str()),
193+
}
194+
}
195+
196+
normalized
197+
}
198+
174199
/// For each public source entry:
175200
///
176201
/// 1. Perform brace expansion

crates/oxide/tests/scanner.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,25 @@ mod scanner {
17611761
assert_eq!(candidates, vec!["content-['abcd/xyz.html']"]);
17621762
}
17631763

1764+
#[test]
1765+
fn test_source_not_can_ignore_symlinked_directory() {
1766+
let dir = tempdir().unwrap().into_path();
1767+
create_files_in(
1768+
&dir,
1769+
&[("directory_a/a.html", "content-['directory_a/a.html']")],
1770+
);
1771+
let _ = symlink(dir.join("directory_a"), dir.join("symlink"));
1772+
1773+
let mut scanner = Scanner::new(vec![
1774+
public_source_entry_from_pattern(dir.clone(), "@source './'"),
1775+
public_source_entry_from_pattern(dir.clone(), "@source not './symlink'"),
1776+
public_source_entry_from_pattern(dir.clone(), "@source not './directory_a'"),
1777+
]);
1778+
let candidates = scanner.scan();
1779+
1780+
assert!(candidates.is_empty());
1781+
}
1782+
17641783
#[test]
17651784
fn test_extract_used_css_variables_from_css() {
17661785
let dir = tempdir().unwrap().into_path();

0 commit comments

Comments
 (0)