Skip to content

Commit 594f567

Browse files
authored
refactor: use sys_traits (#557)
1 parent c13759b commit 594f567

13 files changed

+242
-294
lines changed

.clippy.toml

+38
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,44 @@
22
# away swc's non-zero-indexed based positioning
33
disallowed-methods = [
44
"swc_common::Spanned::span",
5+
{ path = "std::env::current_dir", reason = "File system operations should be done using the sys_traits crate" },
6+
{ path = "std::path::Path::exists", reason = "File system operations should be done using the sys_traits crate" },
7+
{ path = "std::path::Path::canonicalize", reason = "File system operations should be done using the sys_traits crate" },
8+
{ path = "std::path::Path::is_dir", reason = "File system operations should be done using the sys_traits crate" },
9+
{ path = "std::path::Path::is_file", reason = "File system operations should be done using the sys_traits crate" },
10+
{ path = "std::path::Path::is_symlink", reason = "File system operations should be done using the sys_traits crate" },
11+
{ path = "std::path::Path::metadata", reason = "File system operations should be done using the sys_traits crate" },
12+
{ path = "std::path::Path::read_dir", reason = "File system operations should be done using the sys_traits crate" },
13+
{ path = "std::path::Path::read_link", reason = "File system operations should be done using the sys_traits crate" },
14+
{ path = "std::path::Path::symlink_metadata", reason = "File system operations should be done using the sys_traits crate" },
15+
{ path = "std::path::Path::try_exists", reason = "File system operations should be done using the sys_traits crate" },
16+
{ path = "std::path::PathBuf::exists", reason = "File system operations should be done using the sys_traits crate" },
17+
{ path = "std::path::PathBuf::canonicalize", reason = "File system operations should be done using the sys_traits crate" },
18+
{ path = "std::path::PathBuf::is_dir", reason = "File system operations should be done using the sys_traits crate" },
19+
{ path = "std::path::PathBuf::is_file", reason = "File system operations should be done using the sys_traits crate" },
20+
{ path = "std::path::PathBuf::is_symlink", reason = "File system operations should be done using the sys_traits crate" },
21+
{ path = "std::path::PathBuf::metadata", reason = "File system operations should be done using the sys_traits crate" },
22+
{ path = "std::path::PathBuf::read_dir", reason = "File system operations should be done using the sys_traits crate" },
23+
{ path = "std::path::PathBuf::read_link", reason = "File system operations should be done using the sys_traits crate" },
24+
{ path = "std::path::PathBuf::symlink_metadata", reason = "File system operations should be done using the sys_traits crate" },
25+
{ path = "std::path::PathBuf::try_exists", reason = "File system operations should be done using the sys_traits crate" },
26+
{ path = "std::fs::canonicalize", reason = "File system operations should be done using the sys_traits crate" },
27+
{ path = "std::fs::copy", reason = "File system operations should be done using the sys_traits crate" },
28+
{ path = "std::fs::create_dir", reason = "File system operations should be done using the sys_traits crate" },
29+
{ path = "std::fs::create_dir_all", reason = "File system operations should be done using the sys_traits crate" },
30+
{ path = "std::fs::hard_link", reason = "File system operations should be done using the sys_traits crate" },
31+
{ path = "std::fs::metadata", reason = "File system operations should be done using the sys_traits crate" },
32+
{ path = "std::fs::read", reason = "File system operations should be done using the sys_traits crate" },
33+
{ path = "std::fs::read_dir", reason = "File system operations should be done using the sys_traits crate" },
34+
{ path = "std::fs::read_link", reason = "File system operations should be done using the sys_traits crate" },
35+
{ path = "std::fs::read_to_string", reason = "File system operations should be done using the sys_traits crate" },
36+
{ path = "std::fs::remove_dir", reason = "File system operations should be done using the sys_traits crate" },
37+
{ path = "std::fs::remove_dir_all", reason = "File system operations should be done using the sys_traits crate" },
38+
{ path = "std::fs::remove_file", reason = "File system operations should be done using the sys_traits crate" },
39+
{ path = "std::fs::rename", reason = "File system operations should be done using the sys_traits crate" },
40+
{ path = "std::fs::set_permissions", reason = "File system operations should be done using the sys_traits crate" },
41+
{ path = "std::fs::symlink_metadata", reason = "File system operations should be done using the sys_traits crate" },
42+
{ path = "std::fs::write", reason = "File system operations should be done using the sys_traits crate" }
543
]
644
disallowed-types = [
745
"swc_common::BytePos",

Cargo.lock

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ members = ["lib"]
1414

1515
[workspace.dependencies]
1616
deno_unsync = { version = "0.4.0", default-features = false }
17+
sys_traits = "0.1.0"
1718

1819
[lib]
1920
name = "deno_graph"
@@ -44,6 +45,7 @@ capacity_builder = "0.5.0"
4445
data-url = "0.3.0"
4546
deno_ast = { version = "0.44.0", features = ["dep_analysis", "emit"] }
4647
deno_unsync.workspace = true
48+
deno_path_util = "0.3.0"
4749
deno_semver = "0.7.1"
4850
encoding_rs = "0.8.33"
4951
futures = "0.3.26"
@@ -57,6 +59,7 @@ regex = "1.10.2"
5759
serde = { version = "1.0.130", features = ["derive", "rc"] }
5860
serde_json = { version = "1.0.67", features = ["preserve_order"] }
5961
sha2 = "^0.10.0"
62+
sys_traits.workspace = true
6063
thiserror = "2.0.3"
6164
twox-hash = { version = "1.6.3", optional = true }
6265
url = { version = "2.2.2", features = ["serde"] }
@@ -74,6 +77,7 @@ tempfile = "3.4.0"
7477
tokio = { version = "1.10.1", features = ["macros", "rt-multi-thread", "sync"] }
7578
deno_terminal = "0.1.1"
7679
env_logger = "0.11.3"
80+
sys_traits = { workspace = true, features = ["memory"] }
7781

7882
[profile.release]
7983
codegen-units = 1

lib/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ js-sys = "0.3.68"
2323
serde = { version = "1.0.130", features = ["derive", "rc"] }
2424
serde_json = { version = "1.0.67", features = ["preserve_order"] }
2525
serde-wasm-bindgen = "0.5.0"
26+
sys_traits = { workspace = true, features = ["real", "wasm"] }
2627
wasm-bindgen = { version = "=0.2.91" }
2728
wasm-bindgen-futures = { version = "=0.4.41" }
2829

lib/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ pub async fn js_create_graph(
273273
BuildOptions {
274274
is_dynamic: false,
275275
resolver: maybe_resolver.as_ref().map(|r| r as &dyn Resolver),
276+
// todo(dsherret): actually implement this for Wasm users
277+
// and don't just use a RealSys here as it would be better
278+
// to have a way for users to provide their own file system
279+
// via the JS API.
276280
file_system: &NullFileSystem,
277281
jsr_url_provider: Default::default(),
278282
npm_resolver: None,

src/graph.rs

+85-38
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ use std::collections::HashMap;
7373
use std::collections::HashSet;
7474
use std::collections::VecDeque;
7575
use std::fmt;
76+
use std::path::Path;
7677
use std::sync::Arc;
78+
use sys_traits::FileType;
79+
use sys_traits::FsDirEntry;
7780
use thiserror::Error;
7881
use url::Url;
7982
use wasm::wasm_module_to_dts;
@@ -1207,7 +1210,6 @@ pub struct BuildFastCheckTypeGraphOptions<'a> {
12071210
pub workspace_fast_check: WorkspaceFastCheckOption<'a>,
12081211
}
12091212

1210-
#[derive(Default)]
12111213
pub struct BuildOptions<'a> {
12121214
pub is_dynamic: bool,
12131215
/// Additional imports that should be brought into the scope of
@@ -1217,7 +1219,7 @@ pub struct BuildOptions<'a> {
12171219
pub imports: Vec<ReferrerImports>,
12181220
pub executor: &'a dyn Executor,
12191221
pub locker: Option<&'a mut dyn Locker>,
1220-
pub file_system: &'a dyn FileSystem,
1222+
pub file_system: &'a FileSystem,
12211223
pub jsr_url_provider: &'a dyn JsrUrlProvider,
12221224
/// Whether to pass through JSR specifiers to the resolver instead of
12231225
/// resolving them. This is useful in cases where you want to mark JSR
@@ -1229,6 +1231,24 @@ pub struct BuildOptions<'a> {
12291231
pub resolver: Option<&'a dyn Resolver>,
12301232
}
12311233

1234+
impl<'a> Default for BuildOptions<'a> {
1235+
fn default() -> Self {
1236+
Self {
1237+
is_dynamic: false,
1238+
imports: Default::default(),
1239+
executor: Default::default(),
1240+
locker: None,
1241+
file_system: &NullFileSystem,
1242+
jsr_url_provider: Default::default(),
1243+
passthrough_jsr_specifiers: false,
1244+
module_analyzer: Default::default(),
1245+
npm_resolver: None,
1246+
reporter: None,
1247+
resolver: None,
1248+
}
1249+
}
1250+
}
1251+
12321252
#[derive(Debug, Copy, Clone)]
12331253
pub enum ModuleEntryRef<'a> {
12341254
Module(&'a Module),
@@ -2433,7 +2453,7 @@ pub(crate) struct ParseModuleOptions {
24332453
/// With the provided information, parse a module and return its "module slot"
24342454
#[allow(clippy::result_large_err)]
24352455
pub(crate) fn parse_module(
2436-
file_system: &dyn FileSystem,
2456+
file_system: &FileSystem,
24372457
jsr_url_provider: &dyn JsrUrlProvider,
24382458
maybe_resolver: Option<&dyn Resolver>,
24392459
maybe_npm_resolver: Option<&dyn NpmResolver>,
@@ -2493,7 +2513,7 @@ pub(crate) fn parse_js_module_from_module_info(
24932513
maybe_headers: Option<&HashMap<String, String>>,
24942514
module_info: ModuleInfo,
24952515
source: Arc<str>,
2496-
file_system: &dyn FileSystem,
2516+
file_system: &FileSystem,
24972517
jsr_url_provider: &dyn JsrUrlProvider,
24982518
maybe_resolver: Option<&dyn Resolver>,
24992519
maybe_npm_resolver: Option<&dyn NpmResolver>,
@@ -2847,7 +2867,7 @@ fn parse_wasm_module_from_module_info(
28472867
module_info: ModuleInfo,
28482868
source: Arc<[u8]>,
28492869
source_dts: Arc<str>,
2850-
file_system: &dyn FileSystem,
2870+
file_system: &FileSystem,
28512871
jsr_url_provider: &dyn JsrUrlProvider,
28522872
maybe_resolver: Option<&dyn Resolver>,
28532873
maybe_npm_resolver: Option<&dyn NpmResolver>,
@@ -2880,7 +2900,7 @@ fn fill_module_dependencies(
28802900
dependencies: Vec<DependencyDescriptor>,
28812901
module_specifier: &ModuleSpecifier,
28822902
module_dependencies: &mut IndexMap<String, Dependency>,
2883-
file_system: &dyn FileSystem,
2903+
file_system: &FileSystem,
28842904
jsr_url_provider: &dyn JsrUrlProvider,
28852905
maybe_resolver: Option<&dyn Resolver>,
28862906
maybe_npm_resolver: Option<&dyn NpmResolver>,
@@ -3079,7 +3099,7 @@ fn analyze_dynamic_arg_template_parts(
30793099
referrer: &Url,
30803100
referrer_range: &PositionRange,
30813101
import_attributes: &ImportAttributes,
3082-
file_system: &dyn FileSystem,
3102+
file_system: &FileSystem,
30833103
) -> Vec<String> {
30843104
fn resolve_initial_dir_path(
30853105
parts: &[DynamicTemplatePart],
@@ -3169,18 +3189,58 @@ fn analyze_dynamic_arg_template_parts(
31693189
if is_fs_root_specifier(&dir_path) {
31703190
return specifiers;
31713191
}
3192+
let Ok(dir_path) = deno_path_util::url_to_file_path(&dir_path) else {
3193+
return specifiers;
3194+
};
31723195
let mut pending_dirs = VecDeque::from([dir_path]);
3196+
let handle_err = |path: &Path, err: &std::io::Error| {
3197+
if matches!(
3198+
err.kind(),
3199+
std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::NotFound
3200+
) {
3201+
return;
3202+
}
3203+
// For now, errors are swallowed and not stored in the graph.
3204+
// If we decide to represent these in the graph, we'll need to
3205+
// figure out what to do with errors like directory errors.
3206+
// Additionally, take note that these are dynamic import errors,
3207+
// so they shouldn't be eagerly surfaced.
3208+
log::warn!(
3209+
"Graph failed resolving '{}'. {:#}\n at {}:{}:{}",
3210+
path.display(),
3211+
err,
3212+
referrer,
3213+
referrer_range.start.line + 1,
3214+
referrer_range.start.character + 1,
3215+
);
3216+
};
31733217
while let Some(dir_path) = pending_dirs.pop_front() {
3174-
let entries = file_system.read_dir(&dir_path);
3218+
let entries = match file_system.fs_read_dir_boxed(&dir_path) {
3219+
Ok(entries) => entries,
3220+
Err(err) => {
3221+
handle_err(&dir_path, &err);
3222+
continue;
3223+
}
3224+
};
31753225
for entry in entries {
3176-
match entry.kind {
3177-
DirEntryKind::File => {
3178-
let url = &entry.url;
3179-
if matching_media_types.contains(&MediaType::from_specifier(url)) {
3180-
if url == referrer {
3226+
let entry = match entry {
3227+
Ok(entry) => entry,
3228+
Err(err) => {
3229+
handle_err(&dir_path, &err);
3230+
continue;
3231+
}
3232+
};
3233+
let path = entry.path();
3234+
match entry.file_type() {
3235+
Ok(FileType::File) => {
3236+
let Ok(url) = deno_path_util::url_from_file_path(&path) else {
3237+
continue;
3238+
};
3239+
if matching_media_types.contains(&MediaType::from_specifier(&url)) {
3240+
if url == *referrer {
31813241
continue; // found itself, so skip
31823242
}
3183-
if let Some(specifier) = referrer.make_relative(url) {
3243+
if let Some(specifier) = referrer.make_relative(&url) {
31843244
let specifier = if !specifier.starts_with("../") {
31853245
format!("./{}", specifier)
31863246
} else {
@@ -3210,38 +3270,25 @@ fn analyze_dynamic_arg_template_parts(
32103270
}
32113271
}
32123272
}
3213-
DirEntryKind::Dir => {
3273+
Ok(FileType::Dir) => {
32143274
// ignore hidden directories and any node_modules/vendor folders
3215-
let is_allowed_dir = entry
3216-
.url
3217-
.path()
3218-
.rsplit('/')
3219-
.find(|c| !c.is_empty())
3275+
let is_allowed_dir = path
3276+
.file_name()
32203277
.map(|c| {
3221-
!c.starts_with('.') && c != "node_modules" && c != "vendor"
3278+
!c.to_string_lossy().starts_with('.')
3279+
&& c != "node_modules"
3280+
&& c != "vendor"
32223281
})
32233282
.unwrap_or(true);
32243283
if is_allowed_dir {
3225-
pending_dirs.push_back(entry.url);
3284+
pending_dirs.push_back(path.into_owned());
32263285
}
32273286
}
3228-
DirEntryKind::Symlink => {
3287+
Ok(_) => {
32293288
// ignore
32303289
}
3231-
DirEntryKind::Error(err) => {
3232-
// For now, errors are swallowed and not stored in the graph.
3233-
// If we decide to represent these in the graph, we'll need to
3234-
// figure out what to do with errors like directory errors.
3235-
// Additionally, take note that these are dynamic import errors,
3236-
// so they shouldn't be eagerly surfaced.
3237-
log::warn!(
3238-
"Graph failed resolving '{}'. {:#}\n at {}:{}:{}",
3239-
entry.url,
3240-
err,
3241-
referrer,
3242-
referrer_range.start.line + 1,
3243-
referrer_range.start.character + 1,
3244-
);
3290+
Err(err) => {
3291+
handle_err(&path, &err);
32453292
}
32463293
};
32473294
}
@@ -3466,7 +3513,7 @@ impl FillPassMode {
34663513
struct Builder<'a, 'graph> {
34673514
in_dynamic_branch: bool,
34683515
was_dynamic_root: bool,
3469-
file_system: &'a dyn FileSystem,
3516+
file_system: &'a FileSystem,
34703517
jsr_url_provider: &'a dyn JsrUrlProvider,
34713518
passthrough_jsr_specifiers: bool,
34723519
loader: &'a dyn Loader,

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ pub struct ParseModuleOptions<'a> {
115115
pub specifier: ModuleSpecifier,
116116
pub maybe_headers: Option<HashMap<String, String>>,
117117
pub content: Arc<[u8]>,
118-
pub file_system: &'a dyn FileSystem,
118+
pub file_system: &'a FileSystem,
119119
pub jsr_url_provider: &'a dyn JsrUrlProvider,
120120
pub maybe_resolver: Option<&'a dyn Resolver>,
121121
pub maybe_npm_resolver: Option<&'a dyn NpmResolver>,
@@ -158,7 +158,7 @@ pub struct ParseModuleFromAstOptions<'a> {
158158
pub specifier: ModuleSpecifier,
159159
pub maybe_headers: Option<&'a HashMap<String, String>>,
160160
pub parsed_source: &'a deno_ast::ParsedSource,
161-
pub file_system: &'a dyn FileSystem,
161+
pub file_system: &'a FileSystem,
162162
pub jsr_url_provider: &'a dyn JsrUrlProvider,
163163
pub maybe_resolver: Option<&'a dyn Resolver>,
164164
pub maybe_npm_resolver: Option<&'a dyn NpmResolver>,

0 commit comments

Comments
 (0)